雅礼集训2019 Day2

two

雅礼集训2019 Day2_第1张图片
考场写了一个神奇的树链剖分过了???
我的做法简单来讲就是按照题意模拟,对于一条边求出它在另一棵树上可以 b a n ban ban掉的边,然后对于每条重链开 v e c t o r vector vector线段树维护可以删掉的边,修改的时候参考标记永久化即可。
时间复杂度摊下来是 O ( n l o g n 2 ) O(nlogn^2) O(nlogn2)
本来一点都不毒瘤但本地测大数据的时候会爆栈于是写了个bfs版的
代码:

#include
#define ri register int
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (l+r>>1)
#define pb push_back
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=2e5+5;
priority_queue<int,vector<int>,greater<int> >q[2];
int n,que[N],hd,tl;
struct Node{int x,y;};
struct Tree{
	vector<int>e[N],S[N<<2];
	bool vis[N];
	Node G[N];
	int siz[N],fa[N],hson[N],dep[N],top[N],num[N],pred[N],tot;
	Tree(){
		tot=dep[1]=fa[1]=0;
		memset(hson,0,sizeof(hson));
		memset(G,0,sizeof(G));
		memset(vis,0,sizeof(vis));
	}
	void bfs(){
		memset(que,0,sizeof(que));
		que[hd=tl=1]=1;
		while(hd<=tl){
			int p=que[hd++];
			siz[p]=1;
			for(ri i=0,v;i<e[p].size();++i){
				if((v=e[p][i])==fa[p])continue;
				fa[v]=p,dep[v]=dep[p]+1,que[++tl]=v;
			}
		}
		for(ri i=n,p;i>1;--i){
			p=que[i],siz[fa[p]]+=siz[p];
			if(siz[p]>siz[hson[fa[p]]])hson[fa[p]]=p;
		}
		for(ri i=1,p;i<=n;++i){
			p=que[i];
			if(hson[fa[p]]^p)top[p]=p;
			else top[p]=top[fa[p]];
		}
		que[hd=tl=n]=1;
		for(ri i=1;i<=n;++i){
			int p=que[hd++];
			pred[num[p]=++tot]=p;
			if(!hson[p])continue;
			for(ri i=0,v;i<e[p].size();++i){
				if((v=e[p][i])==fa[p]||v==hson[p])continue;
				que[--hd]=v;
			}
			que[--hd]=hson[p];
		}
	}
	inline int lca(int x,int y){
		while(top[x]^top[y]){
			if(dep[top[x]]<dep[top[y]])swap(x,y);
			x=fa[top[x]];
		}
		return dep[x]<dep[y]?x:y;
	}
	inline void update(int p,int l,int r,int ql,int qr,int v){
		if(ql>r||qr<l)return;
		if(ql<=l&&r<=qr){S[p].pb(v);return;}
		if(qr<=mid)update(lc,l,mid,ql,qr,v);
		else if(ql>mid)update(rc,mid+1,r,ql,qr,v);
		else update(lc,l,mid,ql,mid,v),update(rc,mid+1,r,mid+1,qr,v);
	}
	inline void query(int p,int l,int r,int k,int op){
		for(int i=0;i<S[p].size();++i)if(!vis[S[p][i]])vis[S[p][i]]=1,q[op].push(S[p][i]);
		if(l==r)return;
		if(k<=mid)query(lc,l,mid,k,op);
		else query(rc,mid+1,r,k,op);
	}
	inline void init(){
		for(ri i=1;i<=n;++i)e[i].clear();
		for(ri i=1;i<=(n<<2);++i)S[i].clear();
		for(ri i=2,u,v;i<=n;++i)u=read(),e[u].pb(i),e[i].pb(u),G[i-1]=(Node){u,i};
		bfs();
	}
	inline void change(int x,int y,int v){
		if(x==y)return;
		while(top[x]^top[y]){
			if(dep[top[x]]<dep[top[y]])swap(x,y);
			update(1,1,n,num[top[x]],num[x],v),x=fa[top[x]];
		}
		if(x==y)return;
		if(dep[x]<dep[y])swap(x,y);
		update(1,1,n,num[y]+1,num[x],v);
	}
}T[2];
inline void solve(){
	for(ri i=1,u,v,t;i<n;++i){
		u=T[0].G[i].x,v=T[0].G[i].y,t=T[1].lca(u,v);
		T[1].change(u,t,i),T[1].change(v,t,i);
	}
	for(ri i=1,u,v,t;i<n;++i){
		u=T[1].G[i].x,v=T[1].G[i].y,t=T[0].lca(u,v);
		T[0].change(u,t,i),T[0].change(v,t,i);
	}
}
inline void ask(int id,int op){
	int u=T[op].G[id].x,v=T[op].G[id].y;
	if(T[op].dep[u]<T[op].dep[v])swap(u,v);
	T[op].query(1,1,n,T[op].num[u],op^1);
}
int main(){
	n=read();
	T[0].init(),T[1].init();
	int tmp=0;
	q[tmp].push(read()),T[tmp^1].vis[q[tmp].top()]=1;
	solve();
	int tim=0;
	while(q[tmp].size()){
		++tim;
		if(tmp==0)puts("Blue");
		else puts("Red");
		int cnt=0;
		while(!q[tmp].empty()){
			int x=q[tmp].top();
			q[tmp].pop();
			cout<<x<<' ',ask(x,tmp);
		}
		tmp^=1;
		puts("");
	}
	return 0;
}

bracket

雅礼集训2019 Day2_第2张图片
n , m ≤ 5 e 4 n,m\le5e4 n,m5e4
考虑用 + 1 / − 1 +1/-1 +1/1来表示左右括号,然后相当于查和为 0 0 0的路径,考虑用点分治来合并两条路径,那么对于一个点对 ( i , j ) (i,j) (i,j),只用使得 s u m i , g + s u m j , g = 0 sum_{i,g}+sum_{j,g}=0 sumi,g+sumj,g=0即可,于是对于每个点到当前分治重心记录最大/小的前缀和是多少,且这个最值出现了多少次,于是可以将两个拼起来,即 a n s i + j + = f s u m i ∗ g − s u m j ans_{i+j}+=f_{sum}{i}*g_{-sum}{j} ansi+j+=fsumigsumj发现是一个卷积形式,于是上 f f t fft fft优化一波。
注意由于点分治走一层上限小一半因此复杂度并不是 O ( n 2 l o g n ) O(n^2logn) O(n2logn)而是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
代码:

#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
inline int readf(){
	char ch=gc();
	while(ch!='('&&ch!=')')ch=gc();
	return ch==')'?-1:1;
}
typedef long long ll;
const int N=2e5+5;
int n,m,a[N],ans[N];
vector<int>e[N];
struct cp{
	double x,y;
	cp(double x=0,double y=0):x(x),y(y){}
	friend inline cp operator+(const cp&a,const cp&b){return cp(a.x+b.x,a.y+b.y);}
	friend inline cp operator-(const cp&a,const cp&b){return cp(a.x-b.x,a.y-b.y);}
	friend inline cp operator*(const cp&a,const cp&b){return cp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
	friend inline cp operator/(const cp&a,const double&b){return cp(a.x/b,a.y/b);}
};
vector<cp>A,B;
vector<int>pos,f[N<<1],g[N<<1];
int lim,tim;
inline void init(const int&up){
	lim=1,tim=0;
	while(lim<=up)lim<<=1,++tim;
	pos.resize(lim),A.resize(lim),B.resize(lim),pos[0]=0;
	for(ri i=0;i<lim;++i)pos[i]=(pos[i>>1]>>1)|((i&1)<<(tim-1));
}
const double pi=acos(-1.0);
inline void fft(vector<cp>&a,const int&type){
	for(ri i=0;i<lim;++i)if(i<pos[i])swap(a[i],a[pos[i]]);
	cp w,wn,a0,a1;
	for(ri mid=1;mid<lim;mid<<=1){
		wn=cp(cos(pi/mid),sin(pi/mid)*type);
		for(ri j=0,len=mid<<1;j<lim;j+=len){
			w=cp(1,0);
			for(ri k=0;k<mid;++k,w=w*wn){
				a0=a[j+k],a1=a[j+k+mid]*w;
				a[j+k]=a0+a1,a[j+k+mid]=a0-a1;
			}
		}
	}
	if(type==-1)for(ri i=0;i<lim;++i)a[i]=a[i]/lim;
}
int siz[N],all,rt,ms,du;
bool vis[N];
void getroot(int p,int fa){
	siz[p]=1;
	int mss=0;
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa||vis[v])continue;
		getroot(v,p),siz[p]+=siz[v],mss=max(mss,siz[v]);
	}
	mss=max(mss,all-siz[p]);
	if(mss<ms)rt=p,ms=mss;
}
inline void calc(int x,int type){
	int nn=f[all+x].size(),mm=g[all-x].size(),n=0,m=0;
	if(!nn||!mm)return;
	for(ri i=0;i<nn;++i)n=max(n,f[all+x][i]);
	for(ri i=0;i<mm;++i)m=max(m,g[all-x][i]);
	init(n+m);
	for(ri i=0;i<lim;++i)A[i]=B[i]=cp(0,0);
	for(ri i=0;i<nn;++i)++A[f[all+x][i]].x;
	for(ri i=0;i<mm;++i)++B[g[all-x][i]].x;
	fft(A,1),fft(B,1);
	for(ri i=0;i<lim;++i)A[i]=A[i]*B[i];
	fft(A,-1);
	for(ri i=0;i<lim;++i)ans[i+(x!=0)]+=(ll)type*(ll)(A[i].x+0.5);
}
void dfs1(int p,int fa,int pre,int mx,int cnt){
	pre+=a[p],++du;
	if(!pre)++cnt;
	else if(pre==1)pre=cnt=0,++mx;
	if(!pre)f[all+mx].push_back(cnt);
	for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])!=fa&&!vis[v])dfs1(v,p,pre,mx,cnt);
}
void dfs2(int p,int fa,int pre,int mx,int cnt){
	pre+=a[p];
	if(!pre)++cnt;
	else if(pre==-1)pre=cnt=0,--mx;
	if(!pre)g[all+mx].push_back(cnt);
	for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])!=fa&&!vis[v])dfs2(v,p,pre,mx,cnt);
}
inline void delet(int p,int pre){
	du=1;
	if(pre==1)dfs1(p,0,0,1,0);
	else dfs1(p,0,-1,0,0);
	dfs2(p,0,0,0,0);
	for(ri i=0;i<=du;++i)calc(i,-1),f[all+i].clear(),g[all-i].clear();
}
inline void solve(int p){
	vis[p]=1;
	dfs1(p,0,0,0,0);
	int S=all;
	for(ri i=0,v;i<e[p].size();++i)if(!vis[v=e[p][i]])dfs2(v,p,0,0,0);
	g[S].push_back(0);
	for(ri i=0;i<=S;++i)calc(i,1),f[S+i].clear(),g[S-i].clear();
	for(ri i=0,v;i<e[p].size();++i)if(!vis[v=e[p][i]])delet(v,a[p]);
	for(ri i=0,v;i<e[p].size();++i){
		if(vis[v=e[p][i]])continue;
		all=siz[v]>siz[p]?S-siz[p]:siz[v],ms=n+1,getroot(v,p),solve(rt);
	}
}
int main(){
	n=read();
	for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
	for(ri i=1;i<=n;++i)a[i]=readf();
	ms=all=n,getroot(1,0),solve(rt);
	ll sum=(ll)n*n;
	for(ri i=1;i<=n;++i)sum-=ans[i];
	ans[0]=sum;
	for(ri tt=read();tt;--tt)cout<<ans[read()]<<'\n';
	return 0;
}

sum

这个是湖南2013省选模拟原题,神仙结论+费用流。

你可能感兴趣的:(#,题解)