省选测试40

省选测试40

还可以吧,把暴力分打满了。

A. 献给逝去的公主七重奏

分析

首先容易发现可以把同一深度的放在一起考虑,它们的贡献一定是同步的,所以可以把树抽象成一条链,枚举深度然后判断这个深度在当前有没有贡献。

在随机数据下,树的深度比较小,所以可以直接过。

考虑深度比较大的时候怎么做。

对于每一个二进制位分开考虑,发现题目中的操作实际上就是自底向上做了一个前缀和,而且因为是异或操作,所以这个前缀和是模 \(2\) 意义下的。

所以我们只需要快速判断每一个二进制位做 \(k\) 次前缀和之后根节点值的奇偶性即可。

这东西可以从多项式的角度去考虑。

把根节点的 \(dep\) 看成 \(0\),令 \(dep[i]=maxdep-dep[i]\),那么可以构造一个多项式 \(F(x)=\sum_{i=0}^{maxdep}a_ix^i\)

其中 \(a_i\) 是深度为 \(i\) 的所有点的异或和。

做前缀和实际上就是将这个多项式乘上 \(G(x)=1+x^1+...+x^{\infty}\)

最终的答案就是 \(F(x)G(x)^k\)\(maxdep\) 次项的系数。

枚举最终的系数是从多少个 \(G(x)\) 中转移过来的

答案就是

\(\begin{aligned} \sum_{i=0}^{maxdep} a_i \sum_{j=0}^{maxdep-i} \binom{k}{j} \binom{maxdep-i-1}{j-1} &=\sum_{i=0}^{maxdep} a_i \sum_{j=0}^{maxdep-i} \binom{k}{j} \binom{maxdep-i-1}{j-1}\\&=\sum_{i=0}^{maxdep} a_i \sum_{j=0}^{maxdep-i} \binom{k}{k-j} \binom{maxdep-i-1}{j-1} \\&=\sum_{i=0}^{maxdep} a_i \binom{maxdep-i+k-1}{k-1} \end{aligned}\)

因为我们把 \(i\) 看成了 \(maxdep-i\),所以如果再换回原树中的深度,就是 \(\binom{dep+k-1}{k-1}\)

现在的问题就是判断 \(\binom{dep+k-1}{k-1}\) 的奇偶性。

有一个很有用的结论如果 \(n \& k=0\),那么 \(\binom{n+k}{k}\) 为奇数,否则为偶数。

可以简单证明一下。

\(\binom{n+k}{k}=\frac{(n+k)!}{n!k!}\),这个东西的奇偶性实际上取决与分子和分母中 \(2\) 的指数的大小。

这个东西我们在做扩展卢卡斯的时候也求过,具体的做法是一直把当前的数右移,并且把答案加上当前的数,如果 \(n \& k=0\),在二进制下是不进位的,分子和分母得到的结果相同,否则进位之后就会多算贡献。

我们只考虑了一个二进制位的贡献,实际上对于其它二进制位也是一样的。

所以我们对于读进来的 \(k\),只需要统计所有 \(dep \&(k-1)=0\) 的深度的答案即可。

\(FWTor\) 预处理一下就行了。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<bitset>
#define rg register
template<typename T>void read(rg T& x){
	x=0;rg int fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	x*=fh;
}
const int maxn=4e5+5;
int n,q,h[maxn],tot=1,a[maxn],maxdep,c[maxn],dep[maxn],sum[maxn];
struct asd{
	int to,nxt;
}b[maxn];
void ad(rg int aa,rg int bb){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	h[aa]=tot++;
}
void dfs(rg int now,rg int lat){
	dep[now]=dep[lat]+1;
	sum[dep[now]]^=a[now];
	maxdep=std::max(maxdep,dep[now]);
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		dfs(u,now);
	}
}
int main(){
	memset(h,-1,sizeof(h));
	read(n),read(q);
	rg int aa,bb;
	for(rg int i=1;i<n;i++){
		read(aa),read(bb);
		ad(aa,bb),ad(bb,aa);
	}
	for(rg int i=1;i<=n;i++) read(a[i]);
	for(rg int i=1;i<=q;i++) read(c[i]);
	dep[0]=-1;
	dfs(1,0);
	rg int mmax=1;
	for(;mmax<=maxdep;mmax<<=1);
	for(rg int len=1;len<mmax;len<<=1){
		for(rg int j=0,now=len<<1;j<mmax;j+=now){
			for(rg int k=0;k<len;k++){
				sum[j+k+len]^=sum[j+k];
			}
		}
	}
	rg int tmp;
	for(rg int i=1;i<=q;i++){
		if(c[i]==0){
			printf("%d\n",a[1]);
		} else {
			tmp=((c[i]-1)&(mmax-1))^(mmax-1);
			printf("%d\n",sum[tmp]);
		}
	}
	return 0;
}

B. 优雅地绽放吧,墨染的樱花

分析

与度数有关的生成树问题考虑 \(prufer\) 序列。

如果一个点 \(a\)\(prufer\) 序列中出现了 \(i\) 次,那么它对于答案的贡献就是 \(\frac{(i+1)w_a^{i+1}}{i!}\),最后在外边还要乘一个 \((n-2)!\)

最终我们要求的是所有点出现次数之和为 \(n-2\) 的方案数,所以可以对于每一个点构造一个多项式 \(F(a,x)=\sum_{i=0}^{n}\frac{(i+1)w_a^{i+1}}{i!}x^i\)

对于这 \(n\) 个多项式做卷积得到的多项式的 \(n-2\) 次项的系数就是最终的结果。

考虑优化这个过程,把每一个多项式都提出一个 \(w_a\),乘到最外面,答案是不影响的。

\(x=w_ax\),那么 \(F(a,x)=\sum_{i=0}^{n}\frac{(i+1)x^i}{i!}\),

因为 \(e^x=\sum_{i=0}^{+\infty} \frac{x^i}{i!}\)

所以 \((e^xx)'=\sum_{i=0}^{+\infty}\frac{(x^{i+1})'}{i!}=\sum_{i=0}^{+\infty}\frac{(i+1)x^i}{i!}=F(a,x)\)

\((e^xx)'=(e^x)'x+x'e^x=e^xx+e^x=e^x(x+1)\)

所以 \(F(a,x)=e^x(x+1)\)

那么最终的答案就是 \((n-2)! \prod_{i=1}^n w_i\prod_{i=1}^ne^{w_ix}(w_ix+1)\)

后面的部分其实就是两个多项式乘起来,

\(H(x)=\prod_{i=1}^n(w_ix+1),G(x)=e^{\sum_{i=1}^nw_ix}=\sum_{i=0}^{n}\frac{(\sum_{i=1}^nw_ix)^i}{i!}\)

\(H(x)\) 可以用分治乘法 \(nlog^2n\) 去处理,\(G(x)\) 可以直接 \(nlogn\) 预处理。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<vector>
#define rg register
template<typename T>void read(rg T& x){
	x=0;rg int fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	x*=fh;
}
const int maxn=4e5+5,mod=998244353,G=3;
inline int addmod(rg int now1,rg int now2){
	return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
	return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
	return now1*=now2,now1>=mod?now1%mod:now1;
}
inline int ksm(rg int ds,rg int zs){
	rg int nans=1;
	while(zs){
		if(zs&1) nans=mulmod(nans,ds);
		ds=mulmod(ds,ds);
		zs>>=1;
	}
	return nans;
}
int w[25][maxn],wz[maxn];
void ntt(std::vector<int>&A,rg int lim,rg int typ){
	for(rg int i=0;i<lim;i++) if(i<wz[i]) std::swap(A[i],A[wz[i]]);
	for(rg int len=1,t0=0;len<lim;len<<=1,t0++){
		for(rg int j=0,now=len<<1;j<lim;j+=now){
			for(rg int k=0;k<len;k++){
				rg int x=A[j+k],y=mulmod(A[j+k+len],w[t0][k]);
				A[j+k]=addmod(x,y),A[j+k+len]=delmod(x,y);
			}
		}
	}
	if(typ==-1){
		rg int ny=ksm(lim,mod-2);
		std::reverse(A.begin()+1,A.end());
		for(rg int i=0;i<lim;i++) A[i]=mulmod(A[i],ny);
	}
}
std::vector<int> g[maxn*20],f;
int n,a[maxn];
void dfs(rg int da,rg int l,rg int r){
	if(l==r){
		g[da].resize(2);
		g[da][0]=1,g[da][1]=a[l];
		return;
	}
	rg int mids=(l+r)>>1;
	dfs(da<<1,l,mids),dfs(da<<1|1,mids+1,r);
	rg int lim=1,bit=0,len=r-l+1;
	for(;lim<=len<<1;lim<<=1) bit++;
	for(rg int i=0;i<lim;i++) wz[i]=(wz[i>>1]>>1)|((i&1)<<(bit-1));
	g[da<<1].resize(lim),g[da<<1|1].resize(lim),g[da].resize(lim);
	ntt(g[da<<1],lim,1),ntt(g[da<<1|1],lim,1);
	for(rg int i=0;i<lim;i++) g[da][i]=mulmod(g[da<<1][i],g[da<<1|1][i]);
	ntt(g[da],lim,-1);
	for(rg int i=len+1;i<lim;i++) g[da][i]=0;
}
int ans,sum,ny[maxn],jcc[maxn];
int main(){
	read(n);
	rg int lim=1,bit=0;
	for(;lim<=n+n;lim<<=1);
	for(rg int len=1,t0=0;len<lim;len<<=1,t0++){
		w[t0][0]=1,w[t0][1]=ksm(G,(mod-1)/(len<<1));
		for(rg int i=2;i<len;i++) w[t0][i]=mulmod(w[t0][1],w[t0][i-1]);
	}
	ny[1]=1;
	for(rg int i=2;i<=n;i++) ny[i]=mulmod(mod-mod/i,ny[mod%i]);
	jcc[0]=1;
	for(rg int i=1;i<=n;i++) jcc[i]=mulmod(jcc[i-1],ny[i]);
	for(rg int i=1;i<=n;i++) read(a[i]);
	for(rg int i=1;i<=n;i++) sum=addmod(sum,a[i]);
	dfs(1,1,n);
	for(lim=1;lim<=n+n;lim<<=1) bit++;
	for(rg int i=0;i<lim;i++) wz[i]=(wz[i>>1]>>1)|((i&1)<<(bit-1));
	g[1].resize(lim),f.resize(lim);
	for(rg int i=0;i<=n;i++) f[i]=mulmod(ksm(sum,i),jcc[i]);
	ntt(g[1],lim,1),ntt(f,lim,1);
	for(rg int i=0;i<lim;i++) f[i]=mulmod(f[i],g[1][i]);
	ntt(f,lim,-1);
	ans=f[n-2];
	for(rg int i=1;i<=n-2;i++) ans=mulmod(ans,i);
	for(rg int i=1;i<=n;i++) ans=mulmod(ans,a[i]);
	printf("%d\n",ans);
	return 0;
}

C. 竹取飞翔

分析

为了去重,可以把当前路径上的点权全部加上 \(w\),边权全部减去 \(w\)

先对整棵树进行重链剖分,对于每一棵重链开一颗线段树维护最大子段和。

最大子段和的 \(lmax\)\(rmax\) 可以由当前的重链向外延伸。

修改的时候跳重链顺便统计答案。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#define rg register
template<typename T>void read(rg T& x){
	x=0;rg int fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	x*=fh;
}
const int maxn=2e5+5;
int h[maxn],tot=1,n,m;
struct asd{
	int to,nxt;
}b[maxn];
void ad(rg int aa,rg int bb){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	h[aa]=tot++;
}
int siz[maxn],son[maxn],dfn[maxn],dfnc,dep[maxn],tp[maxn],fa[maxn],ldfn[maxn],rdfn[maxn];
std::multiset<long long> s[maxn],ans;
#define sit std::multiset<long long>::iterator
void dfs1(rg int now,rg int lat){
	fa[now]=lat;
	dep[now]=dep[lat]+1;
	siz[now]=1;
	s[now].insert(0),s[now].insert(0);
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		s[now].insert(0),s[now].insert(0);
		dfs1(u,now);
		siz[now]+=siz[u];
		if(son[now]==0 || siz[u]>siz[son[now]]) son[now]=u;
	}
}
void dfs2(rg int now,rg int top){
	tp[now]=top;
	dfn[now]=++dfnc;
	if(son[now]) dfs2(son[now],top);
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==fa[now] || u==son[now]) continue;
		dfs2(u,u);
	}
}
int rt[maxn],cnt;
struct trr{
	int lch,rch;
	long long lmax,rmax,laz,sum,mmax,val;
}tr[maxn*20];
void push_up(rg int da){
	rg int lc=tr[da].lch,rc=tr[da].rch;
	tr[da].lmax=std::max(tr[lc].lmax,tr[lc].sum-tr[da].val+tr[rc].lmax)+tr[da].laz;
	tr[da].rmax=std::max(tr[rc].rmax,tr[rc].sum-tr[da].val+tr[lc].rmax)+tr[da].laz;
	tr[da].sum=tr[lc].sum+tr[rc].sum-tr[da].val+tr[da].laz;
	tr[da].mmax=std::max(std::max(tr[lc].mmax,tr[rc].mmax),tr[lc].rmax+tr[rc].lmax-tr[da].val)+tr[da].laz;
}
int ad(rg int da,rg int l,rg int r,rg int nl,rg int nr,rg int val){
	if(!da) da=++cnt;
	if(nl>=l && nr<=r){
		tr[da].laz+=val,tr[da].lmax+=val,tr[da].rmax+=val,tr[da].mmax+=val,tr[da].sum+=val;
		return da;
	}
	rg int mids=(nl+nr)>>1;
	if(r<=mids) tr[da].lch=ad(tr[da].lch,l,r,nl,mids,val);
	else if(l>mids) tr[da].rch=ad(tr[da].rch,l,r,mids+1,nr,val);
	else {
		tr[da].val+=val;
		tr[da].lch=ad(tr[da].lch,l,r,nl,mids,val);
		tr[da].rch=ad(tr[da].rch,l,r,mids+1,nr,val);
	}
	push_up(da);
	return da;
}
int xg(rg int da,rg int wz,rg int nl,rg int nr,rg long long max1,rg long long max2){
	if(!da) da=++cnt;
	if(nl==nr){
		tr[da].lmax=tr[da].rmax=max1+tr[da].laz;
		tr[da].mmax=max1+max2+tr[da].laz;
		return da;
	}
	rg int mids=(nl+nr)>>1;
	if(wz<=mids) tr[da].lch=xg(tr[da].lch,wz,nl,mids,max1,max2);
	else tr[da].rch=xg(tr[da].rch,wz,mids+1,nr,max1,max2);
	push_up(da);
	return da;
}
long long tmpans[maxn],tmpans2[maxn],laze[maxn];
void updat1(rg int now){
	ans.erase(ans.find(tmpans[now]));
	tmpans[now]=tr[rt[now]].mmax;
	ans.insert(tmpans[now]);
}
long long getmax1(std::multiset<long long> &S){
	return *--S.end();
}
long long getmax2(std::multiset<long long> &S){
	return *----S.end();
}
void updat2(rg int now1,rg int now2,rg int now3){
	s[now2].erase(s[now2].find(tmpans2[now1]));	
	tmpans2[now1]=tr[rt[now1]].lmax-laze[now1];
	s[now2].insert(tmpans2[now1]);	
	rt[now3]=xg(rt[now3],dfn[now2],ldfn[now3],rdfn[now3],getmax1(s[now2]),getmax2(s[now2]));
}
void updat(rg int xx,rg int yy,rg int val){	
	while(tp[xx]!=tp[yy]){
		if(dep[tp[xx]]<dep[tp[yy]]) std::swap(xx,yy);
		rt[tp[xx]]=ad(rt[tp[xx]],dfn[tp[xx]],dfn[xx],ldfn[tp[xx]],rdfn[tp[xx]],val);
		laze[tp[xx]]+=val;
		updat1(tp[xx]);
		updat2(tp[xx],fa[tp[xx]],tp[fa[tp[xx]]]);
		xx=fa[tp[xx]];
	}
	if(dep[xx]<dep[yy])std::swap(xx,yy); 
	rt[tp[yy]]=ad(rt[tp[yy]],dfn[yy],dfn[xx],ldfn[tp[yy]],rdfn[tp[yy]],val);
	updat1(tp[xx]);
	xx=tp[xx];
    for(;fa[xx];updat1(xx=tp[fa[xx]])){
		updat2(xx,fa[xx],tp[fa[xx]]);
	}
}
int x[maxn],y[maxn],w[maxn];
int main(){
	memset(h,-1,sizeof(h));
	read(n),read(m);
	rg int aa,bb,cc;
	for(rg int i=1;i<n;i++){
		read(aa),read(bb);
		ad(aa,bb),ad(bb,aa);
	}
	dfs1(1,0),dfs2(1,1);
	for(rg int i=1;i<=n;i++) ldfn[i]=rdfn[i]=dfn[i];
	for(rg int i=1;i<=n;i++) ldfn[tp[i]]=std::min(ldfn[tp[i]],dfn[i]),rdfn[tp[i]]=std::max(rdfn[tp[i]],dfn[i]);
	for(rg int i=1;i<=n;i++) ans.insert(0);
	rg char ch;
	for(rg int i=1;i<=m;i++){
		scanf(" %c",&ch);
		if(ch=='+'){
			read(aa),read(bb),read(cc);
			x[i]=aa,y[i]=bb,w[i]=cc;
			updat(aa,bb,cc);
		} else {
			read(aa);
			updat(x[aa],y[aa],-w[aa]);
		}
		printf("%lld\n",getmax1(ans));
	}
	return 0;
}
posted @ 2021-03-17 06:36  liuchanglc  阅读(85)  评论(0编辑  收藏  举报