WXHCoder Round 9!(取名风格真是女少口阿 )
T1:献给逝去公主的七重奏
题目大意:
原题:Codechef WEASELTX
树上每个点有权值
w
i
w_i
wi,每次操作为将所有点的权值变为子树中所有点权值的异或和。
Q次询问(相互独立),问
T
T
T次操作后根节点的权值。
题目分析:
考虑一个点在
i
i
i次操作后对它的
j
j
j级祖先的贡献次数
f
i
,
j
f_{i,j}
fi,j
有前缀和
f
i
,
j
=
∑
k
=
0
j
f
i
−
1
,
k
f_{i,j}=\sum_{k=0}^jf_{i-1,k}
fi,j=∑k=0jfi−1,k
这很像组合数路径数的形式,转化一下发现:
f
i
,
j
=
f
i
−
1
,
j
+
f
i
,
j
−
1
f_{i,j}=f_{i-1,j}+f_{i,j-1}
fi,j=fi−1,j+fi,j−1
同时有
f
1
,
j
=
1
f_{1,j}=1
f1,j=1,所以
f
i
,
j
=
(
i
−
1
+
j
j
)
f_{i,j}=\binom {i-1+j} j
fi,j=(ji−1+j)
那么对于同一深度的点我们就知道了它的贡献次数。
而
(
n
m
)
≡
1
(
m
o
d
2
)
\binom nm\equiv1~(\bmod ~2)
(mn)≡1 (mod 2) 当且仅当
n
&
m
=
m
n\&m=m
n&m=m (
m
m
m为1的二进制位
n
n
n都必须为1)
进一步的,
(
n
+
m
m
)
≡
1
(
m
o
d
2
)
\binom {n+m}m\equiv1~(\bmod ~2)
(mn+m)≡1 (mod 2)当且仅当
n
&
m
=
0
n\&m=0
n&m=0
对于
(
T
−
1
+
d
d
)
\binom{T-1+d}d
(dT−1+d),设
d
d
d的最高二进制位为
k
k
k,当
T
>
2
k
T>2^k
T>2k时,
T
−
1
&
d
=
0
T-1\&d=0
T−1&d=0等价于
(
(
T
−
1
)
%
2
k
)
&
d
=
0
((T-1)\%2^k)\&d=0
((T−1)%2k)&d=0
所以只需要对
T
<
=
2
k
T<=2^k
T<=2k预处理答案就可以了。
暴力子集卷积是
O
(
3
k
)
O(3^k)
O(3k)的,可以用
F
W
T
FWT
FWT的
o
r
or
or卷积,就是
O
(
n
log
n
)
O(n\log n)
O(nlogn)了。
Code:
#include<bits/stdc++.h>
#define maxn 270005
using namespace std;
int n,Q,dep[maxn],mxd,s[maxn],ans[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
void dfs(int u,int ff){
mxd=max(mxd,dep[u]);
for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff) dep[v]=dep[u]+1,dfs(v,u);
}
void FMT(int *a,int len){
for(int i=2,l=1;i<=len;l=i,i<<=1)
for(int j=0;j<len;j+=i) for(int k=j;k<j+l;k++)
a[k+l]^=a[k];
}
int main()
{
int x,y;
scanf("%d%d",&n,&Q);
for(int i=1;i<n;i++) scanf("%d%d",&x,&y),line(x,y),line(y,x);
dfs(1,0);
for(int i=1;i<=n;i++) scanf("%d",&x),s[dep[i]]^=x;
int len=1; while(len<=mxd) len<<=1; int all=len-1;
ans[0]=s[0],FMT(s,len);
for(int S=1;S<1<<len;S++) ans[S]=s[all-(S-1)];
while(Q--) scanf("%d",&x),printf("%d\n",ans[x&all]);
}
T2:幽雅的绽放吧,墨染的樱花
题目大意:
经过prufer序列的转化后大意为:给出序列
w
i
w_i
wi,第
k
k
k个多项式是
∑
i
=
0
n
−
2
i
+
1
i
!
w
k
i
+
1
x
i
\sum_{i=0}^{n-2}{i+1\over i!}w_k^{i+1}x^i
i=0∑n−2i!i+1wki+1xi求
n
n
n个多项式乘起来后的
x
n
−
2
x^{n-2}
xn−2的系数。
n
≤
100000
,
m
o
d
=
998244353
n\le100000,\bmod=998244353
n≤100000,mod=998244353
题目分析:
暴力
O
(
n
3
)
O(n^3)
O(n3),
N
T
T
NTT
NTT优化
O
(
n
2
log
n
)
O(n^2\log n)
O(n2logn)。
另外一种做法:
把多项式看做无穷项,最终的多项式可以表示为:
∏
i
w
i
(
w
i
x
+
1
)
e
w
i
x
=
∏
i
w
i
∏
i
(
w
i
x
+
1
)
e
x
∑
w
\prod_iw_i(w_ix+1)e^{w_ix}\\=\prod_i w_i\prod_i(w_ix+1)e^{x\sum w}
i∏wi(wix+1)ewix=i∏wii∏(wix+1)ex∑w
后面的
e
x
∑
w
e^{x\sum w}
ex∑w可以直接展开,中间的式子分治NTT即可。
Code:
#include<bits/stdc++.h>
#define maxn 135005
using namespace std;
const int mod = 998244353;
typedef vector<int> Poly;
int w[maxn],WL,r[maxn],lg[maxn];
int Pow(int a,int b){
int s=1; for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) s=1ll*s*a%mod;
return s;
}
void init(int n){
for(WL=1;WL<=n;WL<<=1); w[0]=1,w[1]=Pow(3,(mod-1)/WL);
for(int i=2;i<=WL;i++) w[i]=1ll*w[i-1]*w[1]%mod,lg[i]=lg[i>>1]+1;
}
inline void upd(int &x){x+=x>>31&mod;}
void NTT(int *a,int len,int flg){
for(int i=0;i<len;i++) if(i<r[i]) swap(a[i],a[r[i]]);
for(int i=2,l=1;i<=len;l=i,i<<=1){
for(int j=0,v,t=WL/i;j<len;j+=i) for(int k=j,o=0;k<j+l;k++,o+=t)
v=1ll*w[flg^1?WL-o:o]*a[k+l]%mod,upd(a[k+l]=a[k]-v),upd(a[k]+=v-mod);
}
if(flg^1) for(int i=0,Inv=Pow(len,mod-2);i<len;i++) a[i]=1ll*a[i]*Inv%mod;
}
int n,m,a[maxn],sum,Pw=1,fac[maxn],inv[maxn];
Poly Mul(const Poly &a,const Poly &b){
static int A[maxn],B[maxn]; int n=a.size(),m=b.size(),len=1<<(lg[n+m-1]+1);
for(int i=0;i<len;i++) A[i]=i<n?a[i]:0, B[i]=i<m?b[i]:0, r[i]=r[i>>1]>>1|(i&1?len>>1:0);
NTT(A,len,1),NTT(B,len,1);
for(int i=0;i<len;i++) A[i]=1ll*A[i]*B[i]%mod;
NTT(A,len,-1);
return Poly(A,A+n+m-1);
}
Poly solve(int i,int l,int r){
if(l==r) {Poly f(2); f[0]=1,f[1]=a[l]; return f;}
int mid=(l+r)>>1;
Poly L=solve(i<<1,l,mid),R=solve(i<<1|1,mid+1,r);
return Mul(L,R);
}
int main()
{
scanf("%d",&n); if(n==1) return puts("0"),0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum=(sum+a[i])%mod,Pw=1ll*Pw*a[i]%mod;
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=2;i<=n;i++) inv[i]=1ll*inv[i]*inv[i-1]%mod;
init(n); Poly F=solve(1,1,n);
int ans=0;
for(int i=0,pw=1;i<n-1;i++,pw=1ll*pw*sum%mod) ans=(ans+1ll*pw*inv[i]%mod*F[n-2-i])%mod;
printf("%d\n",1ll*ans*Pw%mod*fac[n-2]%mod);
}
T3:竹取飞翔
题目描述:
原题:UOJ#268. 【清华集训2016】数据交互
路径交是指点相交。
n
,
m
≤
100000
n,m\le100000
n,m≤100000
题目分析:
两条相交的链,其中一条链的
L
C
A
LCA
LCA必然在另一条链上。
在每个点
u
u
u上存两个值
a
u
,
b
u
a_u,b_u
au,bu,
a
u
a_u
au表示链LCA为
u
u
u的链的权值和,
b
u
b_u
bu表示链经过点
u
u
u但LCA不为
u
u
u的权值和。
接下来的操作这篇博客里面讲的超好。
做一点补充说明(定义都在上面那篇博客中):
动态DP的常见操作重链剖分,然后维护轻儿子,然后线段树维护重链的答案。
全局的答案再用一个(可删)堆维护,每次修改先删掉原来的,再加入新的。
区间修改
b
b
b与
g
g
g无关,比较好写。单点修改
a
a
a时需要往上跳重链修改
g
g
g,
g
g
g的维护同样需要一个堆,而轻儿子对它的贡献就是它线段树的
l
v
lv
lv(
max
(
g
v
+
∑
a
∈
[
l
,
v
]
)
\max(g_v+\sum a\in[l,v])
max(gv+∑a∈[l,v])),同样要先删再修改再添加。重链上单点的
m
x
mx
mx需要用
g
u
+
s
e
c
o
n
d
g
u
+
a
u
+
b
u
g_u+second~g_u+a_u+b_u
gu+second gu+au+bu进行更新,
b
u
b_u
bu就是这个点的
t
a
g
tag
tag
因为线段树的查询都是对整条重链的查询,所以对每条重链分别开线段树就不需要query了,实现也很简单,就动态开点记一下左右儿子和根,范围就是dfs序的范围。
Code:
#include<bits/stdc++.h>
#define maxn 100005
#define LL long long
using namespace std;
int n,m,dep[maxn],fa[maxn],siz[maxn],son[maxn],top[maxn],dfn[maxn],bot[maxn],ln[maxn],tim;
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
inline void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
struct Heap{
priority_queue<LL>A,B;
void erase(LL x){B.push(x);}
void push(LL x){A.push(x);}
LL top(){while(!B.empty()&&A.top()==B.top()) A.pop(),B.pop(); return A.empty()?0:A.top();}
LL sec(){
LL x=top(); if(A.empty()) return 0; A.pop();
LL y=top(); return A.push(x),y;
}
}ans,g[maxn];
struct data{
LL lx,rx,mx,sa,tag;//lx: gv+[l,v] rx: gu+bu+[u,r] mx: maxsubsequence sa:sum of a tag: add of b
void add(LL v){rx+=v,mx+=v,tag+=v;}
data operator + (const data &B){
return (data){max(lx,sa+B.lx), max(B.rx,rx+B.sa), max(mx,max(B.mx,rx+B.lx)), sa+B.sa, 0};
}
}t[maxn<<2];
int rt[maxn],lc[maxn<<2],rc[maxn<<2],sz;
void build(int &i,int l,int r){if(i=++sz,l!=r) build(lc[i],l,l+r>>1),build(rc[i],(l+r>>1)+1,r);}
void dfs1(int u,int ff){
siz[u]=1,dep[u]=dep[fa[u]=ff]+1;
for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff){
dfs1(v,u),siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int tp){
top[u]=tp,ln[dfn[u]=++tim]=u;
if(!son[u]) ans.push(0),bot[tp]=tim,build(rt[tp],dfn[tp],tim);
else dfs2(son[u],tp);
for(int i=fir[u],v;i;i=nxt[i]) if(!top[v=to[i]]) g[u].push(0),dfs2(v,v);
}
void pushdown(int i){if(t[i].tag) t[lc[i]].add(t[i].tag),t[rc[i]].add(t[i].tag),t[i].tag=0;}
void mdf(int i,int l,int r,int x,int y,int v){//b in [x,y] += v
if(x<=l&&r<=y) return t[i].add(v);
int mid=l+r>>1; pushdown(i);
if(x<=mid) mdf(lc[i],l,mid,x,y,v);
if(y>mid) mdf(rc[i],mid+1,r,x,y,v);
t[i]=t[lc[i]]+t[rc[i]];
}
void mdfa(int i,int l,int r,int x,int v){//a at x += v
if(l==r) {t[i].lx+=v,t[i].rx+=v,t[i].mx+=v,t[i].sa+=v; return;}
int mid=l+r>>1; pushdown(i);
x<=mid?mdfa(lc[i],l,mid,x,v):mdfa(rc[i],mid+1,r,x,v);
t[i]=t[lc[i]]+t[rc[i]];
}
void ins(int i,int l,int r,int x){//g changes, recalc.
if(l==r){
t[i].lx=g[ln[x]].top()+t[i].sa;
t[i].rx=t[i].lx+t[i].tag;
t[i].mx=t[i].rx+g[ln[x]].sec();
return;
}
int mid=l+r>>1; pushdown(i);
x<=mid?ins(lc[i],l,mid,x):ins(rc[i],mid+1,r,x);
t[i]=t[lc[i]]+t[rc[i]];
}
#define ERASE ans.erase(t[rt[tp]].mx)
#define PUSH ans.push(t[rt[tp]].mx)
void modify(int u,int v,int w){
int tp;
for(;top[u]!=top[v];u=fa[top[u]]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
tp=top[u];
ERASE; mdf(rt[tp],dfn[tp],bot[tp],dfn[tp],dfn[u],w); PUSH;
}
if(dep[u]<dep[v]) swap(u,v);
if(u!=v){
tp=top[u];
ERASE; mdf(rt[tp],dfn[tp],bot[tp],dfn[v]+1,dfn[u],w); PUSH;
u=v;
}//now u is LCA.
if(fa[tp=top[u]]) g[fa[tp]].erase(t[rt[tp]].lx);
ERASE; mdfa(rt[tp],dfn[tp],bot[tp],dfn[u],w); PUSH;
if(fa[tp]) g[fa[tp]].push(t[rt[tp]].lx);
for(u=fa[tp];u;u=fa[tp]){
if(fa[tp=top[u]]) g[fa[tp]].erase(t[rt[tp]].lx);
ERASE; ins(rt[tp],dfn[tp],bot[tp],dfn[u]); PUSH;
if(fa[tp]) g[fa[tp]].push(t[rt[tp]].lx);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),line(x,y),line(y,x);
dfs1(1,0),dfs2(1,1);
static int x[maxn],y[maxn],z[maxn]; char op[3];
for(int i=1,t;i<=m;i++){
if(scanf("%s",op),op[0]=='+') scanf("%d%d%d",&x[i],&y[i],&z[i]),modify(x[i],y[i],z[i]);
else scanf("%d",&t),modify(x[t],y[t],-z[t]);
printf("%lld\n",ans.top());
}
}