2017 山东二轮集训 Day7 国王

2017 山东二轮集训 Day7 国王

题目大意

给定一棵树,每个点有黑白两种颜色,定义一条简单路径合法当且仅当路径上所有点黑色与白色数量相等,求有多少非空区间 \([L,R]\) ,使得所有编号 \(\in[L,R]\) 的点形成的本质不同的合法简单路径数多于所有编号 \(\notin[L,R]\) 的点形成的本质不同的合法路径树。

题解

考虑所有以 \(x\) 为一个端点的合法简单路径数量为 \(F_x\)
设两端点编号分别位于 \([L,R]\) 之内和之外的路径树为 \(M\)
那么 \([L,R]\) 合法当且仅当 \(\sum\limits_{x\in[L,R]} F_x-M>\sum\limits_{x\notin[L,R]} F_x-M\)
\(\sum\limits_{x\in[L,R]} F_x>\sum\limits_{x\notin[L,R]} F_x\)
显然可以轻松的点分治求出 \(F_x\) ,然后随着 \(R\) 的增大, 极大的合法的 \(L\) 显然单调不减,所以类似滑动窗口一样的求就可以了。

#include<bits/stdc++.h>
#define debug(x) cerr<<#x<<" = "<<x
#define sp <<"  "
#define el <<endl
#define fgx cerr<<"-----------------------------------"<<endl
#define LL long long
#define M 100010
using namespace std;
namespace IO{
	const int BS=(1<<23)+5; int Top=0;
	char Buffer[BS],OT[BS],*OS=OT,*HD,*TL,SS[20]; const char *fin=OT+BS-1;
	char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);} return (HD==TL)?EOF:*HD++;}
	void flush(){fwrite(OT,1,OS-OT,stdout),OS=OT;}
	void Putchar(char c){*OS++ =c;if(OS==fin)flush();}
	void write(int x){
		if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-');
		while(x) SS[++Top]=x%10,x/=10;
		while(Top) Putchar(SS[Top]+'0'),--Top;
	}
	int read(){
		int nm=0,fh=1; char cw=Getchar();
		for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh;
		for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0');
		return nm*fh;
	}
} using namespace IO;
int G[M<<1],mxs[M],n,m,fs[M],nt[M<<1],to[M<<1],tmp,F[M],V[M],sz[M]; bool vis[M]; LL ans=0; 
inline void link(int x,int y){nt[tmp]=fs[x],fs[x]=tmp,to[tmp++]=y;}
#define rep for(int i=fs[x];i!=-1;i=nt[i]) if(to[i]!=last&&!vis[to[i]])
void fdrt(int x,int last,int all,int &RT){
	mxs[x]=0,sz[x]=1; rep fdrt(to[i],x,all,RT),sz[x]+=sz[to[i]],mxs[x]=max(mxs[x],sz[to[i]]);
	if((mxs[x]=max(mxs[x],all-sz[x]))<mxs[RT]) RT=x;
}
int init(int x,int last,int val){int res=abs(val+=V[x]);G[val+M]++,sz[x]=1;rep res=max(res,init(to[i],x,val)); return res;}
void getans(int x,int last,int val,int fh){sz[x]=1,val+=V[x],F[x]+=fh*G[M-val]; rep getans(to[i],x,val,fh),sz[x]+=sz[to[i]];}
inline void calc(int x,int last,int fh){
	int len=init(x,last,last?V[last]:0)+1; getans(x,last,last?0:-V[x],fh);
	for(int i=-len;i<=len;i++) G[i+M]=0;
}
void solve(int x,int all){
	if(all==1){vis[x]=true;return;}int RT=0,last=0;fdrt(x,0,all,RT),x=RT;
	vis[x]=true,calc(x,0,1); rep calc(to[i],x,-1); rep solve(to[i],sz[to[i]]);
}
int main(){
	n=read(),memset(fs,-1,sizeof(fs)),mxs[0]=n;
	for(int i=1;i<=n;i++) V[i]=(read()<<1)-1;
	for(int i=1,x,y;i<n;i++) x=read(),y=read(),link(x,y),link(y,x);
	solve(1,n); LL sum=0; for(int i=1;i<=n;i++) sum+=F[i]; sum>>=1;
	for(int l=1,r=1,now=F[1];r<=n;r++,now+=F[r]){while(now>sum) now-=F[l++]; ans+=l-1;}
	printf("%lld\n",ans); return 0;
}
posted @ 2019-03-29 09:02  OYJason  阅读(269)  评论(0编辑  收藏  举报