prow2012-A3-0015

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

题意:一棵树,C种颜色给出若干询问,每次询问是若在树上加一条边,对树上的点进行染色,有边相邻的两点不能同色,问染色方案比原来少了多少。

首先知道一棵点数为N的树染色方案数是C*(C-1)^(N-1)^,如果连边的话,变成一个环作为root的树,这时总方案数就是环的染色方案*(C-1)^(left_nodes)

现在问题是快速求得连边后的环中顶点个数,用LCA可以离线求得。


{{{


#include <vector>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;

int node[400000],next[400000],edge[100000],tot;

int father[100000],ans[100000];
bool lcavis[100000],visit[100000];

void insert(int a,int b){
    node[tot] = b,next[tot]=edge[a],edge[a] = tot++;
}
int depth[50000];

#define PII pair<int,int>
vector<PII> Q[50000];
void init(int N){
    for (int i=0;i<N;i++)
        father[i] = i,Q[i].clear(),edge[i] = -1,lcavis[i] = visit[i] = false;
}
int findroot(int x){
    if (father[x]==x) return x;
    else return father[x] = findroot(father[x]);
}

void Unoin(int a,int b){
    int fa = findroot(a);
    int fb = findroot(b);
    father[fa] = fb;
}

void calc(int v){
    int t = Q[v].size();
    for (int i=0;i<t;i++)
    {
        int u = Q[v][i].first;
        if (!visit[u]) continue;
        ans[Q[v][i].second] = depth[u]+depth[v]-2*depth[findroot(u)];
    }
}

void LCA(int v){
    lcavis[v] = true;
    for (int i=edge[v];i!=-1;i=next[i]){
        if (!lcavis[node[i]]){
            LCA(node[i]);
            Unoin(node[i],v);
        }
    }
    visit[v] = true;
    calc(v);
}
void dfs(int now,int fa){
    if (fa==-1) depth[now] = 0;
    else 
    depth[now] = depth[fa]+1;
    for (int i=edge[now];i!=-1;i=next[i])
    if (node[i]!=fa)
        dfs(node[i],now);
}
long long mul(int n,int m,int P){
    long long ans = 1,r = n;
    while(m){
        if (m&1) ans*=r,ans%=P;
        r*=r;
        r%=P;
        m>>=1;
    }
    return ans;
}

int N,M,C,P;
long long f[60000];
int main(){
    int i,a,b;
    while(scanf("%d%d%d%d",&N,&M,&C,&P)!=EOF){
        f[1] = 1LL*C%P,f[2] = 1LL*C*(C-1)%P,f[3] = f[2]*(C-2)%P;
        for (i=4;i<=N;i++)
            f[i] = f[i-1]*(C-2)+f[i-2]*(C-1),f[i]%=P;
        init(N);
        tot = 0;
        for (i=1;i<N;i++)
        {
            scanf("%d%d",&a,&b);
            a--,b--;
            insert(a,b);
            insert(b,a);
        }
        for (i=0;i<M;i++){
            scanf("%d%d",&a,&b);
            a--,b--;
            Q[a].push_back(make_pair(b,i));
            Q[b].push_back(make_pair(a,i));
        }
        dfs(0,-1);
        LCA(0);
        long long oldans = C*mul(C-1,N-1,P)%P;
        for (i=0;i<M;i++){
            ans[i]++;
            //printf("%d-------%d %d\n",ans[i],int(f[ans[i]]),int(mul(C-1,N-ans[i],P)));
            long long nowans = f[ans[i]]*mul(C-1,N-ans[i],P)%P;
            printf("%d\n",int((oldans+P-nowans)%P));
        }
    }
}


}}}

题意:一棵树,C种颜色给出若干询问,每次询问是若在树上加一条边,对树上的点进行染色,有边相邻的两点不能同色,问染色方案比原来少了多少。

首先知道一棵点数为N的树染色方案数是C*(C-1)(N-1),如果连边的话,变成一个环作为root的树,这时总方案数就是环的染色方案*(C-1)^(left_nodes)

现在问题是快速求得连边后的环中顶点个数,用LCA可以离线求得。

#include <vector>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;
int node[400000],next[400000],edge[100000],tot;
int father[100000],ans[100000];
bool lcavis[100000],visit[100000];
void insert(int a,int b){
    node[tot] = b,next[tot]=edge[a],edge[a] = tot++;
}
int depth[50000];
#define PII pair<int,int>
vector<PII> Q[50000];
void init(int N){
    for (int i=0;i<N;i++)
        father[i] = i,Q[i].clear(),edge[i] = -1,lcavis[i] = visit[i] = false;
}
int findroot(int x){
    if (father[x]==x) return x;
    else return father[x] = findroot(father[x]);
}
void Unoin(int a,int b){
    int fa = findroot(a);
    int fb = findroot(b);
    father[fa] = fb;
}
void calc(int v){
    int t = Q[v].size();
    for (int i=0;i<t;i++)
    {
        int u = Q[v][i].first;
        if (!visit[u]) continue;
        ans[Q[v][i].second] = depth[u]+depth[v]-2*depth[findroot(u)];
    }
}
void LCA(int v){
    lcavis[v] = true;
    for (int i=edge[v];i!=-1;i=next[i]){
        if (!lcavis[node[i]]){
            LCA(node[i]);
            Unoin(node[i],v);
        }
    }
    visit[v] = true;
    calc(v);
}
void dfs(int now,int fa){
    if (fa==-1) depth[now] = 0;
    else 
    depth[now] = depth[fa]+1;
    for (int i=edge[now];i!=-1;i=next[i])
    if (node[i]!=fa)
        dfs(node[i],now);
}
long long mul(int n,int m,int P){
    long long ans = 1,r = n;
    while(m){
        if (m&1) ans*=r,ans%=P;
        r*=r;
        r%=P;
        m>>=1;
    }
    return ans;
}
int N,M,C,P;
long long f[60000];
int main(){
    int i,a,b;
    while(scanf("%d%d%d%d",&N,&M,&C,&P)!=EOF){
        f[1] = 1LL*C%P,f[2] = 1LL*C*(C-1)%P,f[3] = f[2]*(C-2)%P;
        for (i=4;i<=N;i++)
            f[i] = f[i-1]*(C-2)+f[i-2]*(C-1),f[i]%=P;
        init(N);
        tot = 0;
        for (i=1;i<N;i++)
        {
            scanf("%d%d",&a,&b);
            a--,b--;
            insert(a,b);
            insert(b,a);
        }
        for (i=0;i<M;i++){
            scanf("%d%d",&a,&b);
            a--,b--;
            Q[a].push_back(make_pair(b,i));
            Q[b].push_back(make_pair(a,i));
        }
        dfs(0,-1);
        LCA(0);
        long long oldans = C*mul(C-1,N-1,P)%P;
        for (i=0;i<M;i++){
            ans[i]++;
            //printf("%d-------%d %d\n",ans[i],int(f[ans[i]]),int(mul(C-1,N-ans[i],P)));
            long long nowans = f[ans[i]]*mul(C-1,N-ans[i],P)%P;
            printf("%d\n",int((oldans+P-nowans)%P));
        }
    }
}