[SDOI2017]切树游戏
这道题从昨天下午写到今天下午
我好菜啊
感到绝望
算法一
考虑朴素dp
设\(dp[x][i]\)表示以\(x\)为根的子树中权值\(i\)的子树个数
考虑加入子节点\(y\)的转移
答案即为
复杂度\(O(QNM^2)\)
算法二
注意到可以使用FWT
优化
设\(DP[x]\)表示\(FWT(dp[x])\)
即
注意因为已经\(FWT\)过了
所以这里的乘法是按位相乘
所以那个\(1\)指的是一个全\(1\)的数组
定义Poly
类记录\(128\)个位置的\(dp\)值
FWT
后计算
最后IFWT
回来
复杂度\(O(QNMlogM)\)
算法三
发现这是个动态DP
设\(dfn[x]\)表示\(x\)的\(dfs\)序
\(F[dfn[x]]\)表示\(x\)的DP
值
即
\(f[dfn[x]]\)表示\(x\)轻儿子的DP
值
即
\(G[dfn[x]]\)表示\(x\)子树内的答案
即
\(g[dfn[x]]\)表示\(x\)轻子树内的答案
即
注意以上数组经过FWT
后计算
设\(y\)是\(x\)的重儿子
有转移
设\(dfn[x]=i\)那么\(dfn[y]=i+1\)
有
写成矩阵就是
考虑合并矩阵
发现只需记录\(4\)个位置的值即可
定义Info
类记录矩阵
动态DP
复杂度\(QMlog^2N\)
可以通过
考虑细节
细节一
修改\(x\)的时候\(f[dfn[fa[x]]]\)需要除去原来的贡献
因为是在\(\mod 10007\)意义下求解
所以需要求出逆元
然而\(0\)(\(10007\)的倍数)没有逆元
似乎有一种记录\(0\)个数的做法
但是更自然的想法是对每个节点维护一颗线段树专门维护\(f\)数组
维护像这种不可减的信息都是这样用数据结构维护的
比如最值可以用multiset
维护
所以直接修改线段树即可
并不会成为复杂度瓶颈
但是因为每个点都要开
所以必须动态开点
细节二
最后统计答案乘上初始矩阵\(\left[\begin{matrix}0&0&1 \end{matrix} \right]\)
直接取出答案就行了
不需要实现矩阵乘法
这是套路
细节三
因为是从下往上的dp
也就是线段树是反着维护矩阵的
这是套路
#include<bits/stdc++.h>
using namespace std;
#define gc c=getchar()
#define r(x) read(x)
#define ll long long
template<typename T>
inline void read(T&x){
x=0;T k=1;char gc;
while(!isdigit(c)){if(c=='-')k=-1;gc;}
while(isdigit(c)){x=x*10+c-'0';gc;}x*=k;
}
const int N=3e4+7;
const int M=128|7;
const int p=10007;
inline int add(int a,int b){
return (a+=b)>=p?a-p:a;
}
inline int sub(int a,int b){
return (a-=b)<0?a+p:a;
}
inline int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)(ans*=a)%=p;
(a*=a)%=p;
b>>=1;
}
return ans;
}
int n,m;
int Inv;
struct Poly{
int A[M];
inline void FWT(){
for(int i=1;i<m;i<<=1){
for(int j=0;j<m;j+=(i<<1)){
for(int k=0;k<i;++k){
int u=A[j+k],v=A[i+j+k];
A[j+k]=add(u,v),A[i+j+k]=sub(u,v);
}
}
}
}
inline void IFWT(){
FWT();
for(int i=0;i<m;++i){(A[i]*=Inv)%=p;}
}
inline Poly(){}
inline Poly(int x){
memset(A,0,m<<2);
A[x]=1;
FWT();
}
inline int& operator [] (const int &x){return A[x];}
inline const int& operator [] (const int &x)const {return A[x];}
inline void operator =(const Poly &T){
memcpy(A,T.A,m<<2);
}
inline void operator *= (const Poly &T){
for(int i=0;i<m;++i)(A[i]*=T[i])%=p;
}
inline Poly operator * (const Poly &T)const {
Poly ret;
for(int i=0;i<m;++i)ret[i]=A[i]*T[i]%p;
return ret;
}
inline void operator += (const Poly &T){
for(int i=0;i<m;++i)A[i]=add(A[i],T[i]);
}
inline Poly operator +(const Poly &T)const {
Poly ret;
for(int i=0;i<m;++i)ret[i]=add(A[i],T[i]);
return ret;
}
inline void operator -= (const Poly &T){
for(int i=0;i<m;++i)A[i]=sub(A[i],T[i]);
}
inline Poly operator -(const Poly &T)const {
Poly ret;
for(int i=0;i<m;++i)ret[i]=sub(A[i],T[i]);
return ret;
}
}one,F[N],G[N],g[N];
int root[N];
int cnt[N];
namespace Data{
int tot;
Poly tr[N*10];
int ls[N*10];
int rs[N*10];
int siz[N*10];
#define ls ls[rt]
#define rs rs[rt]
inline void update(int rt){
tr[rt]=tr[ls]*tr[rs];
}
void insert(int &rt,int l,int r,int x,const Poly &v){
if(!rt)rt=++tot;
++siz[rt];
if(l==r)return void(tr[rt]=v);
int mid=(l+r)>>1;
if(x<=mid)insert(ls,l,mid,x,v);
else insert(rs,mid+1,r,x,v);
if(siz[rt]>r-l)update(rt);
}
#undef ls
#undef rs
};
int a[N];
vector<int>E[N];
int fa[N],siz[N],dep[N],son[N];
void dfs1(int x,int f){
fa[x]=f;
siz[x]=1;
dep[x]=dep[f]+1;
for(int i=0;i<E[x].size();++i){
int v=E[x][i];
if(v==f)continue;
dfs1(v,x);
siz[x]+=siz[v];
if(siz[son[x]]<siz[v])son[x]=v;
}
}
int dfs_clock;
int top[N],bot[N],dfn[N],ptn[N];
void dfs2(int x,int t){
top[x]=t;
dfn[x]=++dfs_clock;
ptn[dfs_clock]=x;
if(!son[x])return void(bot[x]=x);
dfs2(son[x],t);
bot[x]=bot[son[x]];
for(int i=0;i<E[x].size();++i){
int v=E[x][i];
if(v==fa[x]||v==son[x])continue;
dfs2(v,v);
}
}
int pos[N];
void dfs3(int x){
F[x]=Poly(a[x]);
for(int i=0;i<E[x].size();++i){
int v=E[x][i];
if(v==fa[x])continue;
dfs3(v);
F[x]*=F[v]+one;
G[x]+=G[v];
}
G[x]+=F[x];
for(int i=0;i<E[x].size();++i){
int v=E[x][i];
if(v==fa[x]||v==son[x])continue;
pos[v]=++cnt[x];
}
cnt[x]++;
Data::insert(root[x],1,cnt[x],cnt[x],Poly(a[x]));
for(int i=0;i<E[x].size();++i){
int v=E[x][i];
if(v==fa[x]||v==son[x])continue;
g[x]+=G[v];
Data::insert(root[x],1,cnt[x],pos[v],F[v]+one);
}
}
struct Info{
Poly a,b,c,d;
inline Info(){};
inline Info(const Poly &f,const Poly &g):a(f),b(f),c(f),d(f+g){};
inline Info(const Poly &A,const Poly &B,const Poly &C,const Poly &D):a(A),b(B),c(C),d(D){}
inline Info operator + (const Info &x){
return Info(a*x.a,a*x.b+b,c*x.a+x.c,c*x.b+d+x.d);
}
inline Poly f(){
return c;
}
inline Poly g(){
return d;
}
}tr[N<<2];
#define ls (rt<<1)
#define rs (rt<<1|1)
inline void update(int rt){
tr[rt]=tr[rs]+tr[ls];
}
void build(int rt,int l,int r){
if(l==r){
int x=ptn[l];
tr[rt]=Info(Data::tr[root[x]],g[x]);
return ;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
update(rt);
}
void modify(int rt,int l,int r,int x,const Info &v){
if(l==r){
tr[rt]=v;
return ;
}
int mid=(l+r)>>1;
if(x<=mid)modify(ls,l,mid,x,v);
else modify(rs,mid+1,r,x,v);
update(rt);
}
Info query(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y)return tr[rt];
int mid=(l+r)>>1;
if(y<=mid)return query(ls,l,mid,x,y);
if(x>mid)return query(rs,mid+1,r,x,y);
return query(rs,mid+1,r,x,y)+query(ls,l,mid,x,y);
}
inline void Change(int x,int y){
a[x]=y;
Data::insert(root[x],1,cnt[x],cnt[x],Poly(a[x]));
while(x){
modify(1,1,n,dfn[x],Info(Data::tr[root[x]],g[x]));
x=top[x];
Info tmp=query(1,1,n,dfn[x],dfn[bot[x]]);
if(fa[x])Data::insert(root[fa[x]],1,cnt[fa[x]],pos[x],Poly(tmp.f()+one));
if(fa[x])g[fa[x]]-=G[x];
G[x]=tmp.g();
if(fa[x])g[fa[x]]+=G[x];
x=fa[x];
}
}
inline int Query(int x){
Poly t=query(1,1,n,dfn[1],dfn[bot[1]]).g();
t.IFWT();
return t[x];
}
inline void init(){
r(n),r(m);Inv=qpow(m,p-2);
for(int i=0;i<m;++i)one[i]=1;
for(int i=1;i<=n;++i){
r(a[i]);
}
for(int i=1;i<n;++i){
int u,v;r(u),r(v);
E[u].push_back(v);
E[v].push_back(u);
}
dfs1(1,0);
dfs2(1,1);
dfs3(1);
build(1,1,n);
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
init();
int q;r(q);
while(q--){
char s[10];
scanf("%s",s);
if(s[0]=='C'){
int x,y;r(x),r(y);
Change(x,y);
}
else {
int k;r(k);
printf("%d\n",Query(k));
}
}
return 0;
}