CodeChef April Challenge 2019题解

传送门

(Maximum Remaining)

对于两个数(a,b),如果(a=b)没贡献,所以不妨假设(a<b),有(a\%b=a),而(b\%a<a)。综上,我们可以发现答案就是严格次大值

//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=1e5+5;
int x,n,mx,mmx;
int main(){
//	freopen("testdata.in","r",stdin);
	n=read();
	fp(i,1,n){
		x=read();
		if(x>mx)mmx=mx,mx=x;
		else if(x<mx&&x>mmx)mmx=x;
	}
	printf("%d
",mmx);
	return 0;
}

(Friend or Girlfriend)

总共区间个数减去不包含的区间个数就可以了

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
int read(char *s){
	R int len=0;R char ch;while(((ch=getc())>'z'||ch<'a'));
	for(s[++len]=ch;(ch=getc())>='a'&&ch<='z';s[++len]=ch);
	return s[len+1]='',len;
}
const int N=1e6+5;
char s[N],c;int n;ll res;
inline ll calc(R int x){return 1ll*x*(x+1)>>1;}
int main(){
//	freopen("testdata.in","r",stdin);
	for(int T=read();T;--T){
		n=read(),read(s),c=getc(),res=calc(n);
		s[0]=s[n+1]=c;
		for(R int i=0,j=1;i<=n;i=j,j=i+1){
			while(s[j]!=c)++j;
			res-=calc(j-i-1);
		}
		printf("%lld
",res);
	}
	return 0;
}

(Fencing)

每块菜地周围先放四个栅栏,然后把所有两块菜地相邻处的两个栅栏全拆了

//minamoto
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R int x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='
';
}
const int N=1e5+5;
struct node{int x,y;}p[N];
inline bool cmp1(const node &a,const node &b){return a.x==b.x?a.y<b.y:a.x<b.x;}
inline bool cmp2(const node &a,const node &b){return a.y==b.y?a.x<b.x:a.y<b.y;}
int n,m,k,res;
int main(){
//	freopen("testdata.in","r",stdin);
	for(int T=read();T;--T){
		n=read(),m=read(),k=read(),res=(k<<2);
		fp(i,1,k)p[i].x=read(),p[i].y=read();
		sort(p+1,p+1+k,cmp1);
		for(R int i=1,j=1;i<=k;++j,i=j){
			while(j<=k&&p[j+1].x==p[i].x)++j;
			fp(l,i,j-1)res-=(p[l+1].y-p[l].y==1)*2;
		}
		sort(p+1,p+1+k,cmp2);
		for(R int i=1,j=1;i<=k;++j,i=j){
			while(j<=k&&p[j+1].y==p[i].y)++j;
			fp(l,i,j-1)res-=(p[l+1].x-p[l].x==1)*2;
		}
		printf("%d
",res);
	}
	return 0;
}

(Subtree Removal)

显然没必要选了一个节点(u)之后再选它的祖先

(sum_u)表示(u)的子树中节点权值之和。我们设(dp_{u,0/1})考虑(u)的子树,(0/1)表示是否删去(u)的子树,所有被删去节点的权值之和加上(k imes X)的最小值,那么最终答案就是(sum_1-min{dp_{1,0},dp_{1,1}})

树形(dp)的过程的话,如果(u)删掉,显然它的子树里的点没必要删,所有(dp_{u,0}=sum_u+x)。如果(u)不删,那么子树里的点随便删不删,即(dp_{u,1}=sum_{(u,v)in E}min{dp_{v,0},dp_{v,1}})

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R int x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='
';
}
const int N=1e5+5;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
ll sum[N],dp[N][2];int n,x;
void dfs(int u,int fa){
	dp[u][0]=0;
	go(u)if(v!=fa)dfs(v,u),sum[u]+=sum[v],dp[u][0]+=min(dp[v][0],dp[v][1]);
	dp[u][1]=sum[u]+x;
}
int main(){
//	freopen("testdata.in","r",stdin);
	for(int T=read();T;--T){
		n=read(),x=read();
		fp(i,1,n)sum[i]=read();
		for(R int i=1,u,v;i<n;++i)u=read(),v=read(),add(u,v),add(v,u);
		dfs(1,0);
		printf("%lld
",sum[1]-min(dp[1][0],dp[1][1]));
		memset(head,0,4*(n+1)),tot=0;
	}
	return 0;
}

(Playing with Numbers)

先说一个一般点的,对于固定的(x)(kx)(mod p)意义下可以表示出所有是(gcd(p,x))的倍数的数

那么一条路径上能表示出的所有的数就是这条路径上所有节点点权的(gcd)与叶子的(m_i)(gcd)的倍数,不妨记为(d_i)。那么答案就是(m_i-d_i)

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
ll read(){
    R ll res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R ll x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]=' ';
}
const int N=1e5+5;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
ll val[N],m[N],ans[N];int st[N],deg[N],top,n;
void dfs(int u,int fa){
	if(deg[u]==1&&u!=1)st[++top]=u,ans[u]=m[u]-__gcd(m[u],val[u]);
	go(u)if(v!=fa)val[v]=__gcd(val[v],val[u]),dfs(v,u);
}
int main(){
//	freopen("testdata.in","r",stdin);
	for(int T=read();T;--T){
		n=read(),top=0;
		for(R int i=1,u,v;i<n;++i)u=read(),v=read(),add(u,v),add(v,u),++deg[u],++deg[v];
		fp(i,1,n)val[i]=read();fp(i,1,n)m[i]=read();
		dfs(1,0);sort(st+1,st+1+top);
		fp(i,1,top)print(ans[st[i]]);sr[++C]='
';
		memset(head,0,(n+1)<<2),memset(deg,0,(n+1)<<2);
		tot=0;
	}
	return Ot(),0;
}

(Kira Loves Palindromes)

分成两种情况考虑,一个是两段长度相同,一个是两段长度不同

两段长度相同的话我们枚举一下(s_1)的末尾字符的位置,把末尾位置之后的那个串建个(SAM),然后再从后往前枚举(s_1)的开头,如果某一个时刻在(SAM)上找不到了就退出

两段长度不同的话,假设(|s_1|>|s_2|),那么(s_1)一定是由反过来的(s_2)加上一个回文串构成的。我们枚举这个回文串的末尾,把后面那一段建一个(SAM),然后再枚举回文串的开头,之后就和长度相同的情况一样了,因为回文串开头前面的位置就是需要的结尾位置

理论复杂度(O(n^3)),不过极限数据都能过……(本地测(1000)(a)都没(T)),那就不管那么多了……

upd:据(zzk)巨巨说,理论复杂度应该是(O({nchoose 3})),大概(10^8),差不多能过……

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(char *s){
	R int len=0;R char ch;while(((ch=getc())>'z'||ch<'a'));
	for(s[++len]=ch;(ch=getc())>='a'&&ch<='z';s[++len]=ch);
	return s[len+1]='',len;
}
const int N=2005;
char s[N],now[N];int p[N],ch[N][26],fa[N],l[N],sz[N];
ll res;int cnt=1,n,las=1;bool is[N];
inline int newnode(int len){return ++cnt,memset(ch[cnt],0,104),fa[cnt]=sz[cnt]=0,l[cnt]=len,cnt;}
void ins(int c){
	int p=las,np=las=newnode(l[p]+1);++sz[np];
    for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
    if(!p)fa[np]=1;
	else{
		int q=ch[p][c];
		if(l[q]==l[p]+1)fa[np]=q;
		else{
			int nq=newnode(l[p]+1);
			memcpy(ch[nq],ch[q],104);
			fa[nq]=fa[q],fa[q]=fa[np]=nq;
			for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
		}
	}
}
int q[N],c[N];
void calc(){
	fp(i,1,cnt)c[i]=0;
	fp(i,1,cnt)++c[l[i]];
	fp(i,1,cnt)c[i]+=c[i-1];
	fd(i,cnt,1)q[c[l[i]]--]=i;
	fd(i,cnt,1)sz[fa[q[i]]]+=sz[q[i]];
	sz[1]=0;
}
void manacher(char *s){
	int len=strlen(s+1);
	fp(i,1,len)now[(i<<1)-1]='%',now[i<<1]=s[i];
	now[len=(len<<1|1)]='%';
	int pos=0,r=0;
	fp(i,1,len){
		p[i]=i<r?min(p[(pos<<1)-i],r-i):1;
		while(i-p[i]>=1&&i+p[i]<=len&&now[i-p[i]]==now[i+p[i]])++p[i];
		cmax(r,i+p[i])?pos=i:0;
	}
}
int main(){
	freopen("testdata.in","r",stdin);
	n=read(s),manacher(s);
	fp(i,1,n-1){
		cnt=0,las=newnode(0);
		fp(j,i+1,n)ins(s[j]-'a');
		calc();
		for(R int p=1,j=i;j;--j){
			if(!ch[p][s[j]-'a'])break;
			p=ch[p][s[j]-'a'],res+=sz[p];
		}
	}
	fp(i,1,n-2){
		cnt=0,las=newnode(0);
		fd(j,i,1)ins(s[j]-'a');
		calc();
		fp(j,i+1,n)is[j]=0;
		int qwq=(i+1)<<1;
		fp(j,qwq,n<<1|1)j-qwq+1<=p[j]?is[((j<<1)-qwq)>>1]=1:0;
		for(R int j=i+1,p=1;j<n;++j,p=1)if(is[j]){
			fp(k,j+1,n){
				if(!ch[p][s[k]-'a'])break;
				p=ch[p][s[k]-'a'],res+=sz[p];
			}
		}
	}
	fd(i,n,3){
		cnt=0,las=newnode(0);
		fp(j,i,n)ins(s[j]-'a');
		calc();
		fd(j,i-1,1)is[j]=0;
		int qwq=(i-1)<<1;
		fd(j,qwq,1)qwq-j+1<=p[j]?is[((j<<1)-qwq)>>1]=1:0;
		for(R int j=i-1,p=1;j>1;--j,p=1)if(is[j]){
			fd(k,j-1,1){
				if(!ch[p][s[k]-'a'])break;
				p=ch[p][s[k]-'a'],res+=sz[p];
			}
		}
	}
	printf("%lld
",res);
	return 0;
}

(Mininum XOR over Tree)

类似于线段树合并,我们搞个(trie)树合并就可以了,复杂度(O(nlog^2n))

//minamoto
#include<bits/stdc++.h>
#define R register
#define inf 0x3f3f3f3f
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R int x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]=' ';
}
const int N=2e5+5,M=(N<<6);
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int rt[N],ch[M][2],id[M],w[N],cnt,n,q,mx,du;
inline int newnode(){return id[++cnt]=inf,ch[cnt][0]=ch[cnt][1]=0,cnt;}
void ins(int p,int c,int u){
	fd(i,19,0){
		if(!ch[p][c>>i&1])ch[p][c>>i&1]=newnode();
		p=ch[p][c>>i&1];
	}
	cmin(id[p],u);
}
void query(int p,int c){
	mx=0;
	fd(i,19,0)if(ch[p][c>>i&1^1])mx^=(1<<i),p=ch[p][c>>i&1^1];
	else p=ch[p][c>>i&1];
	du=id[p];
}
int merge(int x,int y){
	if(!x||!y)return x|y;
	int t=newnode();
	ch[t][0]=merge(ch[x][0],ch[y][0]),
	ch[t][1]=merge(ch[x][1],ch[y][1]),
	id[t]=min(id[x],id[y]);
	return t;
}
void dfs(int u,int fa){
	rt[u]=newnode(),ins(rt[u],w[u],u);
	go(u)if(v!=fa)dfs(v,u),rt[u]=merge(rt[u],rt[v]);
}
inline void clr(){tot=cnt=0;memset(head,0,(n+1)<<2);}
int main(){
//	freopen("testdata.in","r",stdin);
	for(int T=read();T;--T){
		n=read(),q=read(),mx=du=0;
		fp(i,1,n)w[i]=read();
		for(R int i=1,u,v;i<n;++i)u=read(),v=read(),add(u,v),add(v,u);
		dfs(1,0);
		while(q--){
			int u=read()^du,k=read()^mx;
			query(rt[u],k);
			print(du),print(mx),sr[C]='
';
		}
		clr();
	}
	return Ot(),0;
}

(Moving Rectangles (Challenge))

挑战题还是不做了……

(Offer for Chef)

想了几个小时之后,回去重新读题目突然发现

"in each query, the number of slices with toppings does not exceed 50"

也就是说有用的不超过(50)个,然而我一直按(10^5)在那里算……

不读题目太珂怕了……

有用的只有(n=50)个数字,我们考虑按位贪心,简单来说,我们需要判断最终的答案是否是(s)的超集(ps:如果(i)(j)的子集,那么(j)就是(i)的超集)

(is[i][j])表示把前(i)个数分成(j)份,且每一份的和都是(s)的超集,是否可行。转移显然

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
ll read(){
    R ll res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R ll x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='
';
}
const int N=1e5+5;
ll a[N],sum[55],res;int n,k,q,x,top;bool is[55][55];
bool ck(ll s){
	memset(is,0,sizeof(is));
	is[0][0]=1;
	fp(i,0,top-1)fp(j,0,k-1)if(is[i][j]){
		fp(l,i+1,top)if(((sum[l]-sum[i])&s)==s)is[l][j+1]=1;
	}
	return is[top][k];
}
int main(){
//	freopen("testdata.in","r",stdin);
	n=read();
	fp(i,1,n)a[i]=read();
	q=read();
	while(q--){
		top=0,k=read();
		fp(i,1,n){
			x=read();
			if(x)sum[++top]=a[i]*x;
		}
		fp(i,1,top)sum[i]+=sum[i-1];
		if(k>top)sr[++C]='0',sr[++C]='
';
		else{
			res=0;
			fd(i,59,0)if(ck(res|(1ll<<i)))res|=(1ll<<i);
			print(res);
		}
	}
	return Ot(),0;
}

(Edgy)

先考虑暴力,我们判断一下该怎么数纯色子树

因为每一个纯色子树的(LCA)都是唯一的,我们可以在(LCA)处计数。设这棵纯色子树的(LCA)(u)(u)的父亲到它的边的颜色为(c),如果(u)到儿子的边中有一条的颜色不为(c),那么就要(++ans)。即使有多条颜色不为(c)也只加一次,因为这些颜色不同的边是在同一个纯色子树中的。特别的,记(1)的父亲边的颜色为(-1)

这样我们可以打出一个暴力,首先把路径修改拆成两次节点到根的链修改,然后直接暴力修改即可。如果在随机数据下,树高是期望(O(log n))的,复杂度为(O(nlog n))

然而这样显然是(gg)

我们考虑树剖

先假设(u)(u)的重儿子都要被修改。如果(u)(son[u])的颜色不同,那么修改之后(u)的贡献并不会变。如果(u)的轻儿子中两种颜色都有,或者根本没有轻儿子,那么修改之后(u)的贡献也不会变

综上,对于(u)(son[u])都要被修改的情况,(u)的贡献会变化,当且仅当(u)存在轻儿子,且(u)的所有轻儿子颜色相同,并且(u)(son[u])颜色相同。如果之前(u)的贡献为(0),修改之后为(1)。之前为(1),修改之后就为(0)。不满足这些条件的节点都不需要考虑了

我们可以用线段树来维护,记(sz[0])表示区间内贡献为(0)的节点个数,(sz[1])表示区间内贡献为(1)的节点个数。每一次对一条重链的影响就相当于对一个区间取反,也就是说对于每一个区间(swap(sz[0],sz[1])),打懒标记就可以了。顺便修改之后要及时维护答案

接下来是(u)要修改,(son[u])不需要修改的情况,也就是说我们切换了重链。设(v)前一条重链的顶端,也就是(fa[v]=u),那么(v)更新之后,“(u)的所有轻儿子颜色是否相同”这个条件有可能发生变化,而且(u)也被更新,所以“(u)(son[u])的颜色是否相同”这个条件也会发生变化。在线段树上更新就好了

顺带一提,我们在线段树上维护的只有“被链修改之后会改变贡献的点”的贡献,其它的贡献要另算。也就是说(u)(son[u])颜色不同,(u)的轻儿子中两种颜色都有的情况,线段树上是没有的,我们要自己维护

细节巨多,调了整整一个上午……

//minamoto
#include<bits/stdc++.h>
#define inline __attribute__((always_inline))
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R int x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='
';
}
const int N=1e5+5;
struct eg{int v,nx,w;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v,R int w){e[++tot]={v,head[u],w},head[u]=tot;}
inline void swap(R int &x,R int &y){x^=y^=x^=y;}
int col[N],bl[N],top[N],dfn[N],rk[N],sz[N],son[N],dep[N],fa[N],cnt,res;
int sum[N][2];bool ok[N],b[N];
void dfs1(int u,int id){
	sz[u]=1,dep[u]=dep[fa[u]]+1,bl[u]=id;
	go(u)if(v!=fa[u]){
		col[v]=e[i].w,fa[v]=u,dfs1(v,id),sz[u]+=sz[v];
		if(sz[v]>sz[son[u]])son[u]=v;
	}
}
void dfs2(int u,int t){
	rk[dfn[u]=++cnt]=u,top[u]=t,sum[u][0]=sum[u][1]=0;
	if(!son[u])return;
	dfs2(son[u],t);
	go(u)if(!top[v])dfs2(v,v),++sum[u][col[v]];
	if((!sum[u][0]||!sum[u][1])&&(sum[u][0]+sum[u][1])&&col[u]==col[son[u]])
		ok[u]=1,b[u]=(sum[u][col[u]^1]!=0);
	if(sum[u][0]&&sum[u][1]||col[u]!=col[son[u]])++res;
}
struct node{
	node *lc,*rc;int sz[2],c,r;
	inline void ppd(){r^=1,c^=1,swap(sz[0],sz[1]);}
	inline void pd(){if(r)lc->ppd(),rc->ppd(),r=0;}
	inline void upd(){sz[0]=lc->sz[0]+rc->sz[0],sz[1]=lc->sz[1]+rc->sz[1];}
}pool[N<<2],*rt,*pp=pool;
inline node* newnode(){pp->lc=pp->rc=NULL,pp->sz[0]=pp->sz[1]=pp->c=pp->r=0;return pp++;}
void build(node* &p,int l,int r){
	p=newnode();
	if(l==r){
		p->c=col[rk[l]];
		if(ok[rk[l]])p->sz[b[rk[l]]]=1;
		res+=p->sz[1];
		return;
	}
	int mid=(l+r)>>1;
	build(p->lc,l,mid),build(p->rc,mid+1,r);
	p->upd();
}
int query(node *p,int l,int r,int x){
	if(l==r)return p->c;
	int mid=(l+r)>>1;p->pd();
	return x<=mid?query(p->lc,l,mid,x):query(p->rc,mid+1,r,x);
}
void update(node *p,int l,int r,int x,int d){
	if(l==r){
		res-=p->sz[1];
		p->sz[0]=p->sz[1]=0;
		if(d>=0)p->sz[d]=1;
		res+=p->sz[1];
		return;
	}
	int mid=(l+r)>>1;p->pd();
	x<=mid?update(p->lc,l,mid,x,d):update(p->rc,mid+1,r,x,d);
	p->upd();
}
void rev(node *p,int l,int r,int ql,int qr){
	if(ql<=l&&qr>=r)return res-=p->sz[1],res+=p->sz[0],p->ppd(),void();
	int mid=(l+r)>>1;p->pd();
	if(ql<=mid)rev(p->lc,l,mid,ql,qr);
	if(qr>mid)rev(p->rc,mid+1,r,ql,qr); 
	p->upd();
}
int n,q;
inline void clr(){fp(i,1,n)head[i]=top[i]=son[i]=ok[i]=b[i]=0;tot=cnt=0;pp=pool;}
void change(int u){
	int las=-1;
	while(u!=1){
		if(son[u]){
			int c=query(rt,1,n-1,dfn[u]);
			int cc=query(rt,1,n-1,dfn[son[u]]);
			if(c!=cc||(sum[u][0]&&sum[u][1]))--res;
			if(las>=0)--sum[u][las],++sum[u][las^1];
			c^=1;
			if(c!=cc||(sum[u][0]&&sum[u][1]))++res;
			if((!sum[u][0]||!sum[u][1])&&(sum[u][0]+sum[u][1])&&c==cc)
				update(rt,1,n-1,dfn[u],sum[u][c^1^1]!=0);
			else update(rt,1,n-1,dfn[u],-1);
		}
		rev(rt,1,n-1,dfn[top[u]],dfn[u]);
		u=top[u];
		las=query(rt,1,n-1,dfn[u])^1;
		u=fa[u];
	}
	if(las>=0)--sum[u][las],++sum[u][las^1];
}
int main(){
//	freopen("testdata.in","r",stdin);
//	freopen("testdata.out","w",stdout);
	for(int T=read();T;--T){
		n=read(),res=0,sum[1][0]=sum[1][1]=0;
		for(R int i=1,u,v,w;i<n;++i)u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w);
		go(1)fa[v]=1,col[v]=e[i].w,dfs1(v,v),++sum[1][col[v]];
		top[1]=1;go(1)dfs2(v,v);
		res+=(sum[1][0]!=0)+(sum[1][1]!=0);
		build(rt,1,n-1);
		q=read();
		while(q--){
			int u=read(),v=read();
			res-=(sum[1][0]!=0)+(sum[1][1]!=0);
			change(u),change(v);
			res+=(sum[1][0]!=0)+(sum[1][1]!=0);
			print(res);
		}
		clr();
	}
	return Ot(),0;
}

(Sonya and Queries)

等我先学完(ETT)再回来填坑(QAQ)

原文地址:https://www.cnblogs.com/bztMinamoto/p/10711952.html