SDOI2017 切樹遊戲

2022-03-27 01:20:07 字數 3631 閱讀 7231

題目

二輪毒瘤題啊

辣雞洛谷竟然有卡樹剖的資料

還是\(loj\)可愛

首先這道題沒有帶修,設\(dp_\)表示以\(i\)為最高點的連通塊有多少個異或和為\(j\),\(g_=\sum_dp_\) (\(k\in tree(i)\)表示\(k\)在\(i\)子樹內部)

我們可以直接把每乙個權值\(fwt\)一下,大力合併就好了,合併直接對位相乘,只需要在最後\(fwt\)回來就好了

但是我們有了修改,就變成了一道非常噁心的\(ddp\)了

首先我們\(fwt\)肯定還是要\(fwt\)的,我們以下的\(dp\)都是\(fwt\)之後的

考慮我們的方程

\[dp_=dp_+dp_\times dp_

\]\[g_=dp_+g_

\]我們考慮把重兒子和輕兒子分開處理,也就是\(ddp\)了

設\(f'\)表示沒有處理重兒子的\(dp\)陣列,\(g'\)表示沒有處理重兒子的\(g\)陣列

我們可以寫成這樣的矩陣

\[\begin f_\\ 1\\ g_ \end \times \begin f' & f' & 0 \\ 0 & 1 & 0 \\ f' & f'+g' & 1 \end=\begin f_\\ 1\\ g_ \end

\]貓老師的部落格裡提到這個矩陣只有乙個地方是有用的,於是我們可以只存\(4\)個值來表示矩陣,從而大大優化常數

又因為我們\(fwt\)之後可以對於每一位單獨考慮,於是我們直接來上\(128\)棵線段樹分別維護每一位的值就好了

有乙個問題就是我們需要撤回乙個輕兒子的影響

設沒有這個輕兒子的時候為\(f'\),輕兒子影響為\(v\)

則有\[f=f'+f'\times v

\]則有

\[f'=\frac

\]但是如果\(1+v=0\),我們沒有辦法直接除掉這個影響

所以我們還需要對於每乙個點維護出其有多少個輕兒子的會使得\(f\)變成\(0\),以及沒有這些輕兒子的話\(f\)的值應該是多少

這樣我們就能解決這個問題了

但是細節還是有一堆,非常非常難寫

**

#include#include#include#include#define re register

#define ll long long

#define max(a,b) ((a)>(b)?(a):(b))

#define min(a,b) ((a)<(b)?(a):(b))

const int maxn=30005;

const int mod=10007;

const int inv=5004;

inline int read()

struct ee[maxn<<1];

struct mat;

int n,num,len,m,q,__;

int head[maxn],son[maxn],sum[maxn],deep[maxn],fa[maxn],top[maxn],s[128],h[128],inv[mod+5];

int bot[maxn],dp[maxn][128],g[maxn][128],dfn[maxn],id[maxn],pos[maxn],val[maxn][128];

int h[maxn][128],tmp[maxn][128],p[maxn][128];

int l[maxn*3],r[maxn*3];

inline void add(int x,int y)

inline void fwt(int *f,int o)

}int dfs2(int x,int topf)

inline mat operator*(mat a,mat b)

struct segment_tree

mat query(int x,int y,int i)

inline void change(int i,mat k)

}}t[128];

void build(int x,int y,int i)

struct ee[maxn<<1];

struct mat;

int n,num,len,m,q,__;

int head[maxn],son[maxn],sum[maxn],deep[maxn],fa[maxn],top[maxn],s[128],h[128],inv[mod+5];

int bot[maxn],dp[maxn][128],g[maxn][128],dfn[maxn],id[maxn],pos[maxn],val[maxn][128];

int h[maxn][128],tmp[maxn][128],p[maxn][128];

int l[maxn*3],r[maxn*3],a[maxn],b[maxn],top,vis[maxn];

inline void add(int x,int y)

inline void fwt(int *f,int o)

}int dfs2(int x,int topf)

inline mat operator*(mat a,mat b)

struct segment_tree

mat query(const int x,const int y,re int i)

inline void change(re int i,mat k)

}}t[128];

void build(int x,int y,int i)

inline void modify(int x,int v)

else

now.a=(now.a+now.a*g.b)%mod;

now.d=(now.d-now.b+now.a+mod)%mod;

now.d=(now.d-pre.d+g.d+mod)%mod;

now.b=now.c=now.a;

val[x][i]=(val[x][i]*(g.b+1))%mod;

if((g.b+1)%mod==0) tmp[x][i]++;

else h[x][i]=(h[x][i]*(g.b+1))%mod;

pre=t[i].query(dfn[top[x]],dfn[bot[x]],1);

t[i].change(pos[x],now);

if(top[x]==1) break;

g=t[i].query(dfn[top[x]],dfn[bot[x]],1);

x=fa[top[x]];}}

for(re int i=0;i}signed main()

inv[1]=1;

for(re int i=2;ifor(re int x,y,i=1;ix=read(),y=read(),add(x,y),add(y,x);

deep[1]=1;dfs1(1);bot[1]=dfs2(1,1);

build(1,n,1);char op[10];int x,v,x;

q=read();

while(q--)

for(re int i=1;i<=mid;i++) vis[a[i]]=0;

for(re int i=0;ifwt(s,1);

x=read();printf("%d\n",s[x]);

}else

}return 0;

}

SDOI2017 切樹遊戲

一棵樹,每個點有點權,多次操作 1.單點修改乙個點的點權 2.詢問有多少棵子樹點權異或和為 k n leq 30000,k leq 128,q leq 30000 sol 動態 dp 為防止自己忘,再寫一遍 乙個點的 dp 值 sum dp dp 這樣就可以一條重鏈一起轉移 用線段樹維護重鏈上的轉移...

SDOI2017 切樹遊戲

設 f 表示 x 子樹中,所有包含 x 且異或和為 i 的連通塊數量,f 表示 x 子樹中異或和為 i 的連通塊數量。顯然,有公式 f f sum limits x f 現在考慮 f 的轉移。假如我們要合併 x 與某個兒子 y 的dp陣列,則顯然有公式 f sum limits k i f time...

SDOI2017 硬幣遊戲

考慮生成函式來做 g x 函式就是0 0 x 1 x s n s x n 就是最後s位必須填這個串,但是前面隨便填的方案數 然後列舉之前出現了哪個串 包括自己 如果沒有相交,就是fj x g x 還有就是有前字尾有相交部分,pji x 中的第k位,表示i的長度為m k的字首和j的長度為m k的字尾是...