省选测试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;
}