【考试总结】2022-06-05

求和

n 变成奇数并调整求和顺序,设 fi=j=0n2(2ji),gi=j=0n2(2j+1i)

不难发现 fi+gi=(n+1i+1),fi+fi+1=gi+1

据此可以得到顺推式 fi=(n+1i+1)fi12

模数为奇数时可以直接 CRT 合并,模单个因子的组合数可以通过记录次数和剩余部分乘积来计算

模数含有质因子 2 时需要得到逆推式得到的 fi=j=0+(n+1i+2+j)(2)j

但是 j 大于 2 的指数时结果为 0,那么可以暴力计算每一项

Code Display
inline void exgcd(int a,int b,int &x,int &y){
	if(!b) return x=1,y=0,void(); exgcd(b,a%b,x,y);
	int k=x; x=y; y=k-(a/b)*x;
	return ;
}
inline int Inv(int x,int mod){
	int p,q;
	exgcd(x,mod,p,q);
	return (p%mod+mod)%mod;
}
int n,m,mod;
int a[100],M[100],t[100],pcnt;
const int N=1e6+100;
int binom[N],tim[N],pw[N],f[N];
inline int solve(int p,int k,int pk){
	int inv=Inv(2,pk);
	int cur=binom[0]=1,e=0;
	for(int i=1;i<=m+1;++i){
		int tmp=n+2-i;
		while(tmp%p==0) ++e,tmp/=p;
		cur=cur*tmp%pk;
		tmp=i;
		while(tmp%p==0) --e,tmp/=p;
		cur=cur*Inv(tmp,pk)%pk;

		if(e>=k) binom[i]=0;
		else binom[i]=cur*pw[e]%pk;
	}
	f[0]=(n+1)/2;
	for(int i=1;i<=m;++i) f[i]=inv*(binom[i+1]+pk-f[i-1])%pk;
	int sum=0;
	rep(i,0,m) if(!(i&1)) sum+=f[i];
	return sum;
}
inline int solve_2(int k,int pk){
	int cur=binom[0]=1,e=0;
	for(int i=1;i<=m+k;++i){
		int tmp=n+2-i;
		while(!(tmp&1)) ++e,tmp/=2;
		cur=cur*tmp%pk;
		tmp=i;
		while(!(tmp&1)) --e,tmp/=2;
		cur=cur*Inv(tmp,pk)%pk;
		if(e>=k) binom[i]=0;
		else binom[i]=cur*pw[e]%pk;
	}
	int sum=0;
	for(int i=0;i<=m;i+=2){
		int f=0;
		for(int j=0;j<k;++j){
			if(j&1) f-=pw[j]*binom[i+j+2]%pk;
			else f+=pw[j]*binom[i+j+2]%pk;
		}
		sum+=f%pk;
	}
	sum=(sum%pk+pk)%pk;
	return sum;
}
signed main(){
	scanf("%lld%lld%lld",&n,&m,&mod);
	if(mod==1) puts("0"),exit(0);
	if(mod==2) pw[0]=1,printf("%lld\n",solve_2(1,2)),exit(0);
	if(!(n&1)) ++n;
	m=min(n,m);
	int Mod=mod;
	int ans=0;
	for(int i=2;i*i<=Mod;++i) if(Mod%i==0){
		int pk=1,k=0;
		pw[k]=1;
		while(Mod%i==0) pw[++k]=(pk*=i),Mod/=i;
		if(i!=2) a[++pcnt]=solve(i,k,pk);
		else a[++pcnt]=solve_2(k,pk);
		M[pcnt]=mod/pk;
		t[pcnt]=Inv(M[pcnt],pk);
	}
	if(Mod>1){
		pw[0]=1;
		pw[1]=Mod;
		a[++pcnt]=solve(Mod,1,Mod);
		M[pcnt]=mod/Mod;
		t[pcnt]=Inv(M[pcnt],Mod);
	}
	for(int i=1;i<=pcnt;++i) ans=ans+t[i]*a[i]%mod*M[i]%mod;
	cout<<ans%mod<<endl;
	return 0;
}

农民

每个非根节点和其父亲的连边关系本质上是让子树中所有元素 或者 父亲点的权值

可以使用重链剖分来维护合法权值区间,单点修改就是单点修改

对于子树翻转左右儿子的操作,在线段树上维护反向关系支持交换即可

Code Display
const int N=1e5+10;
int n,Q,val[N],Ls[N],Rs[N];
int dfn[N],ord[N],son[N],top[N],siz[N],fa[N],dep[N],tim;
vector<int> G[N];
inline void dfs1(int x,int fat){
	dep[x]=dep[fa[x]=fat]+(siz[x]=1);
	for(auto t:G[x]) if(t!=fat){
		dfs1(t,x); siz[x]+=siz[t];
		if(siz[son[x]]<siz[t]) son[x]=t;
	}
	return ;
}
inline void dfs2(int x,int topf){
	top[x]=topf; ord[dfn[x]=++tim]=x;
	if(son[x]) dfs2(son[x],topf);
	for(auto t:G[x]) if(!dfn[t]) dfs2(t,t);
}
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
const int inf=0x3f3f3f3f3f3f3f3f;
struct interval{
	int l,r;
	interval operator +(const interval &a)const{
		return {max(l,a.l),min(r,a.r)};
	}
}rng[N<<2][2];
int tag[N<<2];
inline void push_up(int p){
	rng[p][0]=rng[ls][0]+rng[rs][0];
	rng[p][1]=rng[ls][1]+rng[rs][1];
	return ;
}
inline void push_tag(int p){swap(rng[p][0],rng[p][1]); tag[p]^=1;}
inline void push_down(int p){
	if(tag[p]){
		push_tag(ls); push_tag(rs);
		tag[p]=0;
	}
}
inline void build(int p,int l,int r){
	if(l==r){
		int x=ord[l];
		if(Rs[fa[x]]==x){
			rng[p][0]={val[fa[x]]+1,inf};
			rng[p][1]={-inf,val[fa[x]]-1};
		}else{
			rng[p][0]={-inf,val[fa[x]]-1};
			rng[p][1]={val[fa[x]]+1,inf};
		}
		return ;		
	}
	int mid=(l+r)>>1;
	build(p<<1,l,mid); build(p<<1|1,mid+1,r);
	return push_up(p);
}
inline void push_rev(int st,int ed,int p=1,int l=2,int r=n){
	if(st<=l&&r<=ed) return push_tag(p);
	int mid=(l+r)>>1; push_down(p);
	if(st<=mid) push_rev(st,ed,lson);
	if(ed>mid) push_rev(st,ed,rson);
	return push_up(p);
}
inline interval query(int st,int ed,int p=1,int l=2,int r=n){
	if(st<=l&&r<=ed) return rng[p][0];
	int mid=(l+r)>>1; push_down(p);
	if(ed<=mid) return query(st,ed,lson);
	if(st>mid) return query(st,ed,rson);
	return query(st,ed,lson)+query(st,ed,rson);
}
inline void Modify(int pos,int v,int p=1,int l=2,int r=n){
	if(l==r){
		if(rng[p][0].l==-inf) rng[p][0].r=v-1;
		else rng[p][0].l=v+1;
		if(rng[p][1].l==-inf) rng[p][1].r=v-1;
		else rng[p][1].l=v+1;
		return ;
	}
	int mid=(l+r)>>1; push_down(p);
	if(pos<=mid) Modify(pos,v,lson);
	else Modify(pos,v,rson);
	return push_up(p);
}
#undef ls
#undef rs
#undef lson
#undef rson
bool mark[N];
int rt;
signed main(){
	n=read(); Q=read(); 
	for(int i=1;i<=n;++i){
		val[i]=read();
		Ls[i]=read(); Rs[i]=read();
		if(Ls[i]){
			mark[Ls[i]]=1;
			G[i].emplace_back(Ls[i]);
		}
		if(Rs[i]){
			mark[Rs[i]]=1;
			G[i].emplace_back(Rs[i]);
		}
	}
	for(int i=1;i<=n;++i) if(!mark[i]){rt=i; break;}
	dfs1(rt,0); 
	dfs2(rt,rt); 
	if(n>1) build(1,2,n);
	while(Q--){
		int opt=read();
		if(opt==1){
			int x=read(),v=read();
			if(Ls[x]) Modify(dfn[Ls[x]],v);
			if(Rs[x]) Modify(dfn[Rs[x]],v);
			val[x]=v;
		}else if(opt==2){
			int x=read();
			if(siz[x]>1) push_rev(dfn[x]+1,dfn[x]+siz[x]-1);
		}else{
			int x=read(),self=val[x];
			interval ans={-inf,+inf};
			while(top[x]!=rt){
				ans=ans+query(dfn[top[x]],dfn[x]);
				x=fa[top[x]];
			}
			if(x!=rt) ans=ans+query(2,dfn[x]);
			if(self>=ans.l&&self<=ans.r) puts("YES");
			else puts("NO");
		}
		
	}
	return 0;
}

仙人掌

建立圆方树,设 fx,i 表示只考虑 x 点在圆方树的子树内边时 xi 条出边的方案数

每个虚点需要合并环上的信息,设 fi,0/1,0/1 为到了环上第 i 个点,环顶连出的第一条边的方向,上一条边的方向;转移需要根据每个环上点的 ai 判断使用哪些 f 的和

对于实点可以将每个虚儿子的信息表示成 ft,0+ft,1x+ft,2x2 其中 ft,i 表示环上有 i 条边连向环顶,使用分治乘法合并即可

Code Display
int n,m,nds;
int a[N],deg[N];
vector<int> g[N],G[N];
int low[N],dfn[N],tim,stk[N],top;
map<pair<int,int> ,int>mp;
inline void ins(int x,int t,int id){
	if(x>t) swap(x,t);
	mp[{x,t}]=id;
}
inline int query(int x,int t){
	if(x>t) swap(x,t);
	if(mp.count({x,t})) return mp[{x,t}];
	return -1;
}
bool mark[N];
inline void tarjan(int x){
	dfn[x]=low[x]=++tim; stk[++top]=x;
	for(auto t:g[x]){
		if(!dfn[t]){
			tarjan(t); ckmin(low[x],low[t]);
			if(low[t]>=dfn[x]){
				++nds; 
				G[x].emplace_back(nds);
				G[nds].emplace_back(x);
				ins(x,t,nds);
				do{
					G[stk[top]].emplace_back(nds);
					G[nds].emplace_back(stk[top]);
					--top;
				}while(stk[top+1]!=t);
			}
		}else{
			int id=query(x,t);
			if(id==-1) ckmin(low[x],dfn[t]);
			else mark[id]=1;
		}
	}
}
inline vector<int> Mul(vector<int> a,vector<int> b){
	int n=a.size(),m=b.size();
	int lim=1; while(lim<=n+m) lim<<=1;
	NTT(a,lim,1); NTT(b,lim,1);
	vector<int> c(lim);
	rep(i,0,lim-1) c[i]=mul(a[i],b[i]);
	NTT(c,lim,-1);
	c.resize(n+m-1);
	return c;
}
vector<int> dp[N];
int f[N][3],ar[N][3];
inline vector<int> solve(int l,int r){
	if(l==r) return {ar[l][0],ar[l][1],ar[l][2]};
	int mid=(l+r)>>1;
	return Mul(solve(l,mid),solve(mid+1,r));
}
int tmp[N][2][2];
// first edge direction
// current edge direction
inline void DP(int x,int fat){
	for(auto t:G[x]) if(t!=fat) DP(t,x);
	if(x<=n){
		int m=0;
		for(auto t:G[x]) if(t!=fat){
			++m;
			rep(i,0,2) ar[m][i]=f[t][i];	
		}
		if(m) dp[x]=solve(1,m);
		else dp[x]={1};
		dp[x].resize(a[x]+1);
		for(int i=0;i<=a[x]-2;++i) ckadd(f[x][2],dp[x][i]);
		f[x][1]=add(f[x][2],dp[x][a[x]-1]);
		f[x][0]=add(f[x][1],dp[x][a[x]]);
	}else{
		if(G[x].size()==2&&!mark[x]){
			for(auto t:G[x]) if(t!=fat){
				f[x][0]=f[t][1];
				f[x][1]=f[t][0];
				return ;
			}
		}
		vector<int> son;
		for(auto t:G[x]) if(t!=fat) son.emplace_back(t);
		int num=son.size();
		tmp[0][0][0]=f[son[0]][1];
		tmp[0][0][1]=f[son[0]][2];
		tmp[0][1][0]=f[son[0]][0];
		tmp[0][1][1]=f[son[0]][1];
		for(int i=1;i<num;++i){
			tmp[i][0][0]=tmp[i][0][1]=tmp[i][1][0]=tmp[i][1][1]=0;
			int t=son[i];
			for(int s=0;s<=1;++s){
				for(int c=0;c<=1;++c) if(tmp[i-1][s][c]){
					ckadd(tmp[i][s][0],mul(f[t][!c],tmp[i-1][s][c]));
					ckadd(tmp[i][s][1],mul(f[t][(!c)+1],tmp[i-1][s][c]));
				}
			}
		}
		f[x][2]=tmp[num-1][1][0];
		f[x][1]=add(tmp[num-1][1][1],tmp[num-1][0][0]);
		f[x][0]=tmp[num-1][0][1];
	}
	return ;
}
signed main(){
	nds=n=read(); m=read();
	for(int i=1;i<=m;++i){
		int u=read(),v=read();
		deg[u]++; deg[v]++;
		g[u].emplace_back(v); g[v].emplace_back(u);
	}
	for(int i=1;i<=n;++i) a[i]=min(read(),2+deg[i]);
	tarjan(1);
	DP(1,0);
	print(f[1][0]);
	return 0;
}

posted @   没学完四大礼包不改名  阅读(88)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示