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\n",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]='\0',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\n",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]='\n';
}
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\n",res);
}
return 0;
}
\(Subtree\ Removal\)
显然没必要选了一个节点\(u\)之后再选它的祖先
记\(sum_u\)表示\(u\)的子树中节点权值之和。我们设\(dp_{u,0/1}\)考虑\(u\)的子树,\(0/1\)表示是否删去\(u\)的子树,所有被删去节点的权值之和加上\(k\times 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]='\n';
}
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\n",sum[1]-min(dp[1][0],dp[1][1]));
memset(head,0,4*(n+1)),tot=0;
}
return 0;
}
\(Playing\ with\ Numbers\)
先说一个一般点的,对于固定的\(x\),\(kx\)在\(\bmod 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]='\n';
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({n\choose 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]='\0',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\n",res);
return 0;
}
\(Mininum\ XOR\ over\ Tree\)
类似于线段树合并,我们搞个\(trie\)树合并就可以了,复杂度\(O(n\log^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]='\n';
}
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]='\n';
}
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]='\n';
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(n\log 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]='\n';
}
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\)
CodeChef April Challenge 2019题解的更多相关文章
- Codechef April Challenge 2019 游记
Codechef April Challenge 2019 游记 Subtree Removal 题目大意: 一棵\(n(n\le10^5)\)个结点的有根树,每个结点有一个权值\(w_i(|w_i\ ...
- CodeChef March Challenge 2019题解
传送门 \(CHNUM\) 显然正数一组,负数一组 for(int T=read();T;--T){ n=read(),c=d=0; fp(i,1,n)x=read(),x>0?++c:++d; ...
- Codechef April Challenge 2019 Division 2
Maximum Remaining 题意:给n个数,取出两个数$a_{i}$,$a_{j}$,求$a_{i}\% a_{j}$取模的最大值 直接排个序,第二大(严格的第二大)模第一大就是答案了. #i ...
- CodeChef November Challenge 2019 Division 1题解
传送门 AFO前的最后一场CC了--好好打吧-- \(SIMGAM\) 偶数行的必定两人平分,所以只要抢奇数行中间那个就行了 这题怎么被爆破了 //quming #include<bits/st ...
- Codechef July Challenge 2019 Division 1题解
题面 \(CIRMERGE\) 破环成链搞个裸的区间\(dp\)就行了 //quming #include<bits/stdc++.h> #define R register #defin ...
- Code Chef February Challenge 2019题解
传送门 \(HMAPPY2\) 咕 话说这题居然卡\(scanf\)的么??? int T;cin>>T; while(T--){ cin>>n>>a>> ...
- Codechef November Challenge 2019 Division 1
Preface 这场CC好难的说,后面的都不会做QAQ 还因为不会三进制位运算卷积被曲明姐姐欺负了,我真是太菜了QAQ PS:最后还是狗上了六星的说,期待两(三)场之内可以上七星 Physical E ...
- Codechef October Challenge 2019 Division 1
Preface 这次CC难度较上两场升高了许多,后面两题都只能借着曲明姐姐和jz姐姐的仙气来做 值得一提的是原来的F大概需要大力分类讨论,结果我写了一大半题目就因为原题被ban了233 最后勉强涨了近 ...
- Codechef September Challenge 2019 Division 2
Preface 这确实应该是我打过的比较水的CC了(其实就打过两场) 但由于我太弱了打的都是Div2,所以会认为上一场更简单,其实上一场Div的数据结构是真的毒 好了废话不多说快速地讲一下 A Eas ...
随机推荐
- 迷你MVVM框架 avalonjs 1.2发布
avalon1.2 带来了许多新特性,让开发更轻松!详见如下: 升级路由系统与分页组件. 对ms-duplex的绑定值进行增强,以前只能prop或prop.prop2,现在可以prop["x ...
- JAVA程序中使用正则表达式
import java.util.regex.Matcher;import java.util.regex.Pattern; /** * @author Administrator 测试正则表达式 * ...
- hue database is locked
hue使用mysql作为元数据库 hue默认使用sqlite作为元数据库,不推荐在生产环境中使用.会经常出现database is lock的问题. 解决方法: 其实官网也有解决方法,不过过程似乎有点 ...
- Linux基石【第二篇】虚拟网络三种连接方式(转载)
在虚拟机上安装完Centos系统后,开始配置静态IP,以方便在本宿主机上可以访问虚拟机,在曲折的配置中,了解到虚拟机还有三种连接方式:Bridged,NAT和Host-only,于是,我又一轮新的各种 ...
- store下载文件保存位置
PC:C:\Users\accountName\AppData\Roaming\Unity\Asset Store MAC:"~/Library/Unity/Asset"
- style="width:100px" 和width=100 异同
异: 1.width属性不是每个元素都支持的,一般就table和body支持. 2.style="width: 100px"是CSS样式. 2.1.CSS样式有多种方式设置,直接写 ...
- mybatis Generator 生成配置文件
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE generatorConfiguration ...
- java中double和float精度丢失问题
为什么会出现这个问题呢,就这是java和其它计算机语言都会出现的问题,下面我们分析一下为什么会出现这个问题:float和double类型主要是为了科学计算和工程计算而设计的.他们执行二进制浮点运算,这 ...
- ZOJ3700 Ever Dream 2017-04-06 23:22 76人阅读 评论(0) 收藏
Ever Dream Time Limit: 2 Seconds Memory Limit: 65536 KB "Ever Dream" played by Nigh ...
- Geronimo 叛逆者: 使用集成软件包:Codehaus 的 Woodstox(转载)
XML 解析器通常是高性能.健壮应用程序的关键.传统的 XML 解析技术包括文档对象模型(Document Object Model,DOM)和 Simple API for XML (SAX).现在 ...