zoukankan      html  css  js  c++  java
  • 省选测试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)) 中转移过来的

    答案就是

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

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

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

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

    可以简单证明一下。

    (inom{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
    ",a[1]);
    		} else {
    			tmp=((c[i]-1)&(mmax-1))^(mmax-1);
    			printf("%d
    ",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_iprod_{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
    ",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
    ",getmax1(ans));
    	}
    	return 0;
    }
    
  • 相关阅读:
    【Spark学习笔记】第一章 Windows10(64位)主机上搭建基于IntelliJ IDEA的spark+scala开发环境
    【Oracle数据库相关】Oracle数据库在CentOS6.5环境下的安装以及相关操作
    【SNMP】Linux系统下安装net-snmp
    【SNMP】SNMP概述
    【转】 memwatch使用说明书
    [转]GCC参数详解
    【Linux C中文函数手册】 字符串转换函数
    【Linux C中文函数手册】文件内容控制函数
    【Linux C中文函数手册】之 目录操作函数
    [心智历练]---高效、静心、持续
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/14547087.html
Copyright © 2011-2022 走看看