From Tree to Graph(hdu6280 在线LCA+并查集)

From Tree to Graph

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 327680/327680 K (Java/Others)
Total Submission(s): 87    Accepted Submission(s): 22



Problem Description
Bobo has a tree of n vertices numbered with 0,1,,(n1).
He subsequently adds m edges between vertex xi and LCA(xi,yi)
where LCA(xi,yi) is the vertex lying on the unique tree path between vertex xi and yi and closest to the vertex 0.

Let the graph obtained by adding the edges {(x1,LCA(x1,y1)),(x2,LCA(x2,y2)),,(xi,LCA(xi,yi))} to the tree be Gi,
and fi(u) be the number of connected components after the removal of vertex u from Gi.
Bobo knows that for i{0,1,2,,m}
Zi=fi(0)^fi(1)^...^fi(n-1)

( denotes xor.)

Given a,b,x0,y0, he also knows that for i{1,2,,m},

* xi = (a*x(i-1)+b*y(i-1)+z(i-1)) mod n;
* yi = (b*x(i-1)+a*y(i-1)+z(i-1)) mod n;

Help him to find xm, ym.
 

Input
The input consists of several test cases and is terminated by end-of-file.

The first line of each test case contains six integers n, m, a, b, x0, y0.
The i-th of the following (n1) lines contains two integers ui and vi, which denotes the tree edge between vertex ui and vi.
 

Output
For each test case, print two integers which denote xm, ym.

## Constraint

* 2n5000
* 1mn2
* 0a,b,x0,y0,ui,vi<n
* The sum of n does not exceed 25,000.
 

Sample Input
 
  
4 1 1 0 2 30 11 20 34 2 1 0 2 00 11 22 35 25 1 2 3 40 10 21 31 4
 
Sample Output
 
 
2 31 31 0


题意:给定一棵树,现在有2个递推公式:

* xi = (a*x(i-1)+b*y(i-1)+z(i-1)) mod n;

* yi = (b*x(i-1)+a*y(i-1)+z(i-1)) mod n;


现在,x0,y0已知,zi是第i次中,移除第j个点(j=1,2,...,n)后联通图个数的异或和,每次往图中添加一条边(xi,LCA(xi,yi)),求xm和ym,m已知。


思路

通过分析得出,一棵树中去掉一个点后联通图的个数等于这个点的出度。因此,我们可以计算出初始情况下每个点去掉后能得到的联通图个数的数组sz[ ]。


通过画图可以看出,添加一条边后肯定会出现一个环,并且如果移除的是环外的点或这个是添的那条边的2个端点的话,得到的联通图个数是跟前一次一样的,完全没有影响。如果移除环中的点(不包括那2个端点),那么得到的联通图个数会比前一次少1个,所以我们相应的sz[i]也要减1。此外,通过这个计算后,要通过并查集把这个环中的点合并(缩点),因为一个环中的点的效果相当与一个点,可通过画图体会(环中的点不包括LCA(xi,yi))。


细节:用并查集往上走时,有可能会跳过LCA,所以要用depth来判断是否结束。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;

typedef long long ll;

const int MAX = 10000+100;

int n,m,a,b,x,y;
vector<int>mp[MAX];
int faz[MAX];
int sz[MAX];
int pr[MAX];
int ans;

int log2(int x)
{
    int k=0;
    while (x>1)
    {
        x/=2;
        k++;
    }
    return k;
}

//*************LCA*************

int tot;
int id[2*MAX];        //保存DFS时每个编号对应的节点编号
int depth[2*MAX];     //保存DFS时每个编号对应的节点深度
int ss[MAX];          //保存每个节点在DFS时第一次出现的编号
int dp[2*MAX][30];    //ST预处理时的数组,用以查询区间里深度最小的编号(DFS序编号)

void dfs(int u,int pre,int dep)
{
    id[++tot]=u;
    ss[u]=tot;
    depth[tot]=dep;
    faz[u]=pre;
    for(int i=0;i<mp[u].size();i++)
    {
        int v=mp[u][i];
        if(v==pre) continue;
        dfs(v,u,dep+1);
        id[++tot]=u;
        depth[tot]=dep;
    }
}

void ST(int nn)      //n一般取2*n-1(即tot)
{
    int k=(int)(log2(1.0*nn));
    for(int i=1;i<=nn;i++) dp[i][0]=i;
    for(int j=1;j<=k;j++)
    for(int i=1;i+(1<<j)-1<=nn;i++)
    {
        int a=dp[i][j-1];
        int b=dp[i+(1<<(j-1))][j-1];
        if(depth[a]<depth[b]) dp[i][j]=a;
        else dp[i][j]=b;
    }
}

int RMQ(int l,int r)
{
    int k=(int)(log2(1.0*r-l+1));
    int a=dp[l][k];
    int b=dp[r-(1<<k)+1][k];
    if(depth[a]<depth[b]) return a;
    else return b;
}

int LCA(int x,int y)
{
    int l=ss[x];
    int r=ss[y];
    if(l>r) swap(l,r);
    return id[RMQ(l,r)];
}
//*****************************


//***********并查集*************
int Find(int x)
{
    return pr[x]==x?x:pr[x]=Find(pr[x]);
}

void Merge(int x,int y)
{
    x=Find(x);
    //注意:depth[i]中 i 表示的是一个节点的dfs序!!!
    if(depth[ss[faz[x]]]<=depth[ss[y]]||faz[x]==-1)
        return;
    //第一个异或相当与把前一次计算该点时的结果去掉,因为异或2次相当与没异或
    //后一个异或才是本次该点的结果
    ans=ans^sz[faz[x]]^(sz[faz[x]]-1);
    sz[faz[x]]--;
    pr[x]=faz[x];
    Merge(faz[x],y);
}
//*****************************

int main()
{
    while(scanf("%d%d%d%d%d%d",&n,&m,&a,&b,&x,&y)!=EOF)
    {
        ans=0;
        tot=0;
        for(int i=0;i<=n;i++)
        {
            mp[i].clear();
            pr[i]=i;
        }
        for(int i=0;i<n-1;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            u++;
            v++;
            mp[u].push_back(v);
            mp[v].push_back(u);
        }
        dfs(1,-1,1);
        ST(tot);
        for(int i=1;i<=n;i++)
        {
            sz[i]=mp[i].size();
            ans^=sz[i];
        }
        for(int i=0;i<m;i++)
        {
            int bx=(a*x+b*y+ans)%n;
            int by=(b*x+a*y+ans)%n;
            x=bx;
            y=by;
            Merge(x+1,LCA(x+1,y+1));
        }
        printf("%d %d\n",x,y);
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/luyehao1/article/details/81021845