至今我们仍不知道这题是怎么卡过的
RedLycoris · · 题解
题目意思很清楚不用说。
看到每个点都有颜色,然后询问和颜色的种类有关,时限开了很【】的 8s,就可以往根号分治方面想了。
按照常理,我们钦定一个
如果
如果
同理,如果
最后,如果
上述全部为口胡,如果实现不精细的话,时间复杂度会写成
-
普通的倍增LCA的询问是
O(logn) 的,我们要用 ST 表做到O(nlogn) 预处理,O(1) 求 LCA。 -
在建立虚树的时候,有一步要对所有点按照 dfs 序排序。如果不想写基数排序怎么办?可以在询问前先对每个
col_i 中的元素先按照字典序排序,在询问的时候用类似归并排序的方法 merge 两个有序的col_x 和col_y ,就可以把这个logn 砍掉了。
现在的时间复杂度已经降到了正好的
为了减小常数:
-
加入快读快输
-
小对小和大对大中暴力统计答案的dfs看起来很累赘,那么短。如果我们能够想办法把这一步放入建立虚树的过程中,那么就可以避免存虚树的边,从而大幅减小常数(感谢 w23c3c3 的指导)
然后这样就能过了。
Talk is cheap, show me the code.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define reg register
const int maxn=10005; //很好的快读板子
char buffer[maxn],*S,*T;
inline char Get_Char(){
if(S==T){
T=(S=buffer)+fread(buffer,1,maxn,stdin);
if(S==T)return EOF;
}
return *S++;
}
inline int read(){
reg char c;
reg int re=0,f=0;
for(c=Get_Char();c<'0' or c>'9';c=Get_Char())if(c=='-')f=1;
for(;c>='0' and c<='9';)re=(re<<1)+(re<<3)+(c-'0'),c=Get_Char();
if(f)return -re;
return re;
}
inline void read(int&x){
reg char c;
reg int re=0,f=0;
for(c=Get_Char();c<'0' or c>'9';c=Get_Char())if(c=='-')f=1;
for(;c>='0' and c<='9';)re=(re<<1)+(re<<3)+(c-'0'),c=Get_Char();
if(f)x=-re;
else x=re;
}
inline void read(ll&x){
reg char c;
reg ll re=0,f=0;
for(c=Get_Char();c<'0' or c>'9';c=Get_Char())if(c=='-')f=1;
for(;c>='0' and c<='9';)re=(re<<1)+(re<<3)+(c-'0'),c=Get_Char();
if(f)x=-re;
else x=re;
}
inline void print(int x){
if(x<10){
putchar(x+'0');
return;
}
print(x/10);
putchar(x%10+'0');
}
const int mxn=2e5+5;
vector<int>g[mxn];
int n,R,q,r[mxn];
int dep[mxn*2];
int ord[mxn*2],cco,cord[mxn*2];
int mi[mxn*2][22];
int lg[mxn*2],co;
int st[mxn*2],ed[mxn*2];
inline void dfs(int x,int par=0,int deep=1){
cord[++cco]=x;
st[x]=cco;ed[x]=cco;
dep[x]=deep;ord[x]=++co;
for(int y:g[x]){
if(par==y)continue;
dfs(y,x,deep+1);
cord[++cco]=x;
ed[x]=cco;
}
}
inline void init(){ //预处理ST表
lg[1]=0;
for(int i=2;i<mxn*2;++i)lg[i]=lg[i>>1]+1;
for(int i=1;i<=cco;++i)mi[i][0]=cord[i];
for(int k=1;k<22;++k){
for(int i=1;i<=cco-(1<<k)+1;++i){
int x=mi[i][k-1],y=mi[i+(1<<(k-1))][k-1];
if(dep[x]<dep[y])mi[i][k]=x;
else mi[i][k]=y;
}
}
}
inline int lca(int x,int y){
int fx=st[x],fy=ed[y];
if(fx>fy)swap(fx,fy);
int ax=mi[fx][lg[fy-fx]],ay=mi[fy-(1<<lg[fy-fx])+1][lg[fy-fx]];
if(dep[ax]<dep[ay])return ax;
else return ay;
}
inline bool cmp(int x,int y){return ord[x]<ord[y];}
int sta[mxn],top=0;
int sz[mxn];
int up[404][mxn],dw[404][mxn];
inline vector<int>mer(vector<int>x,vector<int>y){ //类似归并的操作
vector<int>rt;rt.clear();
int i=0,j=0;
for(;i<x.size() and j<y.size();)
if(cmp(x[i],y[j]))rt.push_back(x[i]),++i;
else rt.push_back(y[j]),++j;
for(;i<x.size();++i)rt.push_back(x[i]);
for(;j<y.size();++j)rt.push_back(y[j]);
return rt;
}
inline void gen(vector<int>v,int y){ //建立虚树
top=0;
sta[++top]=1;
sz[1]=r[1]==y;
for(int i:v){
if(i!=1){
int l=lca(sta[top],i);
if(l!=sta[top]){
for(;ord[l]<ord[sta[top-1]];)sz[sta[top-1]]+=sz[sta[top]],--top;
if(ord[l]>ord[sta[top-1]]){
sz[l]=r[l]==y;
sz[l]+=sz[sta[top]];
sta[top]=l;
}else sz[l]+=sz[sta[top]],top--;
}
sz[i]=r[i]==y;
sta[++top]=i;
}
}
for(int i=top-1;i>=1;--i)sz[sta[i]]+=sz[sta[i+1]];
}
vector<int>col[mxn];
vector<int>hea;
int ans[404][404];
int id[mxn];
//inline void getans(int x,int fa,int cl){//on ng[] //原来的暴力统计小对小和大对大的答案
// if(r[x]==cl)sz[x]=1;
// else sz[x]=0;
// for(int y:ng[x])if(y!=fa)getans(y,x,cl),sz[x]+=sz[y];
//}
inline void prep(int x,int fa,int cl,int flg=0){ //预处理小对大和大对小的up和dw数组
if(r[x]==hea[cl])dw[cl][x]=1,++flg;
up[cl][x]=flg;
for(int y:g[x])if(y!=fa){
prep(y,x,cl,flg);
dw[cl][x]+=dw[cl][y];
}
}
inline void solve(){
memset(id,-1,sizeof(id));
read(n),read(R),read(q);
read(r[1]);col[r[1]].push_back(1);
for(int i=2;i<=n;++i){
int x;read(x),read(r[i]);
g[x].push_back(i);
g[i].push_back(x);
col[r[i]].push_back(i);
}
dfs(1);
init();
int bound=500;
for(int i=1;i<=R;++i)if(col[i].size()>=bound)hea.push_back(i); //这里我的B定为了500
for(int i=0;i<hea.size();++i)id[hea[i]]=i;
for(int i=1;i<=n;++i)sort(col[i].begin(),col[i].end(),cmp);
for(int i=0;i<hea.size();++i)for(int j=0;j<hea.size();++j)if(i!=j){
gen(mer(col[hea[i]],col[hea[j]]),hea[j]);
// getans(1,0,hea[j]);
ll res=0;
for(int f:col[hea[i]])res+=sz[f];
ans[i][j]=res;
}
for(int i=0;i<hea.size();++i)prep(1,0,i);
for(;q--;){
int x,y;read(x),read(y);
if(~id[x] and ~id[y])print(ans[id[x]][id[y]]),putchar('\n');
else if(~id[x] and id[y]==-1){
ll res=0;
for(int i:col[y])res+=up[id[x]][i];
print(res),putchar('\n');
}else if(id[x]==-1 and ~id[y]){
ll res=0;
for(int i:col[x])res+=dw[id[y]][i];
print(res),putchar('\n');
}else{
gen(mer(col[x],col[y]),y);
// getans(1,0,y);
ll res=0;
for(int f:col[x])res+=sz[f];
print(res),putchar('\n');
}
}
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int T=1;
//cin>>T;
for(;T--;)solve();
return 0;
}