【点分治】【FFT】2019雅礼集训 bracket

题目:

在这里插入图片描述


分析:

太过套路这里就简单说说
显然,当询问数k很小时,就是个裸的点分治题。
现在k变大了。那就FFT算贡献。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
typedef long long ll;
const double Pi=acos(-1);
vector<int> a[MAXN];
bool used[MAXN];
int siz[MAXN],siz1[MAXN];
struct cpx{
	double r,i;
	cpx () {}
	cpx(double r1,double i1):r(r1),i(i1) {}
	cpx operator +(const cpx &a) const {
		return cpx(r+a.r,i+a.i);	
	}
	cpx operator -(const cpx &a) const {
		return cpx(r-a.r,i-a.i);	
	}
	cpx operator *(const cpx &a) const { 
		return cpx(r*a.r-i*a.i,i*a.r+r*a.i);
	}
	cpx operator *(double b) const {
		return cpx(r*b,i*b);	
	}
};
void FFT(cpx A[],int N,int flag){
	for(int i=1,j=0;i<N;i++){
		for(int d=N;j^=d>>=1,~j&d;);
		if(i<j)
			swap(A[i],A[j]);	
	}
	for(int i=1;i<N;i<<=1){
		cpx wn=cpx(cos(Pi/i),sin(Pi/i));
		if(flag)
			wn.i*=-1.0;
		for(int j=0;j<N;j+=(i<<1)){
			cpx w=cpx(1,0);
			for(int k=0;k<i;k++,w=w*wn){
				cpx x=A[j+k],y=w*A[i+j+k];
				A[j+k]=x+y;
				A[i+j+k]=x-y;
			}
		}
	}
	if(flag)
		for(int i=0;i<N;i++)
			A[i].r=A[i].r/double(N);
}
void mul(int A[],int B[],int N,int M,int res[]){
	static cpx A1[MAXN],B1[MAXN];
	for(int i=0;i<N;i++) A1[i].r=A[i];
	for(int i=0;i<M;i++) B1[i].r=B[i];
	int p=1;
	while(p<=N+M)
		p<<=1;
	FFT(A1,p,0);
	FFT(B1,p,0);
	for(int i=0;i<p;i++)
		A1[i]=A1[i]*B1[i];
	FFT(A1,p,1);
	for(int i=0;i<N+M;i++)	
		res[i]=int(A1[i].r+0.5);
	for(int i=0;i<p;i++)
		A1[i].r=A1[i].i=B1[i].r=B1[i].i=0;
}
void get_sz(int x,int fa=0){
	siz[x]=1;
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(u==fa||used[u])
			continue;
		get_sz(u,x);
		siz[x]+=siz[u];
	}
}
int get_maxs(int x,int sizall,int fa=0){
	int res=0;
	siz1[x]=sizall-siz[x];
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(u==fa||used[u])
			continue;
		int res1=get_maxs(u,sizall,x);
		if(res==0||siz1[res1]<siz1[res])
			res=res1;	
		siz1[x]=max(siz1[x],siz[u]);
	}
	if(res==0||siz1[x]<siz1[res])
		res=x;
	return res;
}
int find_g(int x){
	get_sz(x);
	return get_maxs(x,siz[x]);	
}
vector<int> f1[MAXN],f2[MAXN];
ll ans[MAXN];
int val[MAXN],f[MAXN],g[MAXN];
int dfs(int x,int fa,int maxd,int mind,int s1,int s2,int pre=0){
	pre+=val[x];
	int res=1;
	if(maxd<pre){
		maxd=pre;
		f1[pre].push_back(0);
		s1=1;
	}
	else if(maxd==pre){
		f1[pre].push_back(s1);
		s1++;
	}
	if(mind>pre){
		mind=pre;
		f2[-pre].push_back(0);
		s2=1;
	}
	else if(mind==pre){
		f2[-pre].push_back(s2);
		s2++;
	}
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(used[u]||u==fa)
			continue;
		res=max(res,dfs(u,x,maxd,mind,s1,s2,pre)+1);	
	}
	return res;
}
void calc(int md,int flag,int Gval){
	int maxt=0,radd=0,ladd=0;
	if(flag==1){
		f1[0].push_back(0);
		f2[0].push_back(0);
	}
	if(Gval==1)
		radd=1;
	else
		ladd=1;
	for(int i=0;i<=md;i++){
		if(f1[i+ladd].size()&&f2[i+radd].size()){
			maxt=0;
			for(int j=0;j<int(f1[i+ladd].size());j++){
				f[f1[i+ladd][j]]++;
				maxt=max(maxt,f1[i+ladd][j]+1);
			}
			for(int j=0;j<int(f2[i+radd].size());j++){
				g[f2[i+radd][j]+1]++;
				maxt=max(maxt,f2[i+radd][j]+1+1);
			}
//			for(int i=0;i<maxt;i++)
//				PF("[%d %d]\n",f[i],g[i]);
//				PF("~~~~~~~~~~~~~~~~~~~\n");
			mul(f,g,maxt,maxt,f);
			for(int j=0;j<2*maxt;j++){
				ans[j]+=(f[j]*flag);
				f[j]=g[j]=0;
			}
		}
		f1[i].clear();
		f2[i].clear();
	}
}
void solve(int x){
	int G=find_g(x);
	used[G]=1;
	int maxd=0;
//	PF("[%d]\n",G);
	for(int i=0;i<int(a[G].size());i++){
		int u=a[G][i];
		if(used[u])
			continue;
		maxd=max(maxd,dfs(u,G,0,0,1,1));
	}
//	for(int i=0;i<=maxd;i++)
//		PF("[%d %d]\n",f1[i].size(),f2[i].size());
//	PF("======================\n");
	calc(maxd,1,val[G]);
//	for(int i=1;i<7;i++)
//		PF("[%lld]",ans[i]);
//	PF("\n");
	for(int i=0;i<int(a[G].size());i++){
		int u=a[G][i];
		if(used[u])
			continue;
		int maxd1=dfs(u,G,0,0,1,1);
		calc(maxd1,-1,val[G]);
//		PF("{%d -> %d}\n",G,u);
//		for(int i=0;i<7;i++)
//			PF("[%lld]",ans[i]);
//		PF("\n");
	}
	for(int i=0;i<int(a[G].size());i++){
		int u=a[G][i];
		if(used[u])
			continue;
		solve(u);
	}
}
int n,m,u,v;
char s[20];
int main(){
	SF("%d",&n);
	for(int i=1;i<n;i++){
		SF("%d%d",&u,&v);
		a[u].push_back(v);
		a[v].push_back(u);	
	}
	for(int i=1;i<=n;i++){
		SF("%s",s);
		if(s[0]=='(') val[i]=1;
		else val[i]=-1;
	}
	solve(1);
	SF("%d",&m);
	for(int i=1;i<=m;i++){
		SF("%d",&u);
		PF("%lld\n",ans[u]);
	}
//	for(int i=1;i<=n;i++)
//		PF("%lld\n",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值