日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【九省联考2018】秘密袭击【树形dp】【生成函数】【线段树合并】【多项式插值】

發布時間:2023/12/4 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【九省联考2018】秘密袭击【树形dp】【生成函数】【线段树合并】【多项式插值】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

題意:nnn 個點的帶點權的樹,點權最大值為 www,求所有連通子圖第 kkk 大權值之和模 641236412364123。

n,w≤1666n,w\leq 1666n,w1666,時限 5s。

idea 很好的題,可惜被暴力艸過去了。

首先如果點權只有 000111,我們相當于求包含至少 kkk111 的連通子圖個數。可以設 f(u,k)f(u,k)f(u,k) 表示 uuu 為根的可空連通子圖中有恰好 kkk111 的數量,然后就是刻在 DNA 里的轉移方程

f(u,k)?∑i=0kf(u,i)g(v,k?i)f(u,k)\longleftarrow\sum_{i=0}^kf(u,i)g(v,k-i)f(u,k)?i=0k?f(u,i)g(v,k?i)

f(u,0)?f(u,0)+1f(u,0)\longleftarrow f(u,0)+1f(u,0)?f(u,0)+1

邊界: f(u,valu)=1f(u,val_u)=1f(u,valu?)=1

我們枚舉值域,大于等于的設為 111,小于的設為 000,算出方案數乘上差值,就可以 O(n2w)\Omicron(n^2w)O(n2w) 踩標算了。

注意到這個轉移方程是卷積的形式,所以可以寫成生成函數

fu(x)=∏v∈son(u)fv(x)+1f_u(x)=\prod_{v\in son(u)}f_v(x)+1fu?(x)=vson(u)?fv?(x)+1

然后你要求所有點的和,所以開個 ggg 順便記一下

gu(x)=∑v∈son(u)gv(x)+fu(x)?1g_u(x)=\sum_{v\in son(u)}g_v(x)+f_u(x)-1gu?(x)=vson(u)?gv?(x)+fu?(x)?1

所有的生成函數都是 nnn 次的,所以考慮帶 n+1n+1n+1 個點值進去算。因為不是 NTT 模數,所以只能帶一般的點值最后再插值回來。

這樣還是 O(n2w)\Omicron(n^2w)O(n2w) 的。帶點值這步不好優化,但后面的枚舉值域看起來可以優化一下。

可以動態 dp,但常數巨大,還沒暴力跑得快。

注意到不同的閥值的計算過程是一樣的,可以考慮用線段樹合并維護所有閥值的答案。

開個長度為值域的線段樹,我們在線段樹的每個葉子記錄這個閥值的 (f,g)(f,g)(f,g),初值為 (0,0)(0,0)(0,0)。你需要維護以下操作:

  • 開頭的 fff 區間加和區間乘。
  • 合并兩棵線段樹,fff 對應位置相乘, ggg 對應位置相加。
  • 最后的 f←f+1,g←g+ff\leftarrow f+1,g\leftarrow g+fff+1,gg+f
  • 手玩一個標記 (a,b,c,d)(a,b,c,d)(a,b,c,d) 表示 (f,g)?(af+b,cf+g+d)(f,g)\longleftarrow (af+b,cf+g+d)(f,g)?(af+b,cf+g+d),就可以做了。

    關于有懶標記的線段樹合并:線段樹合并的過程中兩邊有一邊沒有兒子時直接返回,這樣 pushdown 次數和遞歸次數同階,不影響復雜度。

    最后插值即可。

    復雜度 O(wnlog?w)\Omicron(wn\log w )O(wnlogw)

    #include <iostream> #include <cstring> #include <cctype> #include <cstdio> #include <vector> #include <algorithm> #define MAXN 2005 using namespace std; inline int read() {int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans; } const int MOD=64123; typedef long long ll; inline int add(const int& x,const int& y){return x+y>=MOD? x+y-MOD:x+y;} inline int dec(const int& x,const int& y){return x<y? x-y+MOD:x-y;} vector<int> e[MAXN]; inline int qpow(int a,int p) {int ans=1;while (p){if (p&1) ans=(ll)ans*a%MOD;a=(ll)a*a%MOD,p>>=1;}return ans; } struct data {int a,b,c,d;inline data(const int& a=1,const int& b=0,const int& c=0,const int& d=0):a(a),b(b),c(c),d(d){} }; inline data operator *(const data& x,const data& y){return data((ll)x.a*y.a%MOD,((ll)y.a*x.b+y.b)%MOD,((ll)x.a*y.c+x.c)%MOD,((ll)x.b*y.c+x.d+y.d)%MOD);} int n,k,w,val[MAXN]; int ch[MAXN<<5][2],cnt; data tag[MAXN<<5]; inline void clear() {for (int i=1;i<=cnt;i++) ch[i][0]=ch[i][1]=0,tag[i]=data();cnt=0; } inline void pushtag(int x,const data& v){tag[x]=tag[x]*v;} inline void pushdown(int x) {if (!ch[x][0]) ch[x][0]=++cnt;if (!ch[x][1]) ch[x][1]=++cnt;pushtag(ch[x][0],tag[x]),pushtag(ch[x][1],tag[x]);tag[x]=data(); } int merge(int x,int y) {if (!x||!y) return x|y;if (!ch[x][0]&&!ch[x][1]) swap(x,y);if (!ch[y][0]&&!ch[y][1]){tag[x]=tag[x]*data(tag[y].b,0,0,0);tag[x]=tag[x]*data(1,0,0,tag[y].d);return x;}pushdown(x),pushdown(y);ch[x][0]=merge(ch[x][0],ch[y][0]);ch[x][1]=merge(ch[x][1],ch[y][1]);return x; } void modify(int& x,int l,int r,int ql,int qr,data v) {if (!x) x=++cnt;if (ql<=l&&r<=qr) return pushtag(x,v);if (qr<l||r<ql) return;int mid=(l+r)>>1;pushdown(x);modify(ch[x][0],l,mid,ql,qr,v),modify(ch[x][1],mid+1,r,ql,qr,v); } int querysum(int x,int l,int r) {if (l==r) return tag[x].d;int mid=(l+r)>>1;pushdown(x);return add(querysum(ch[x][0],l,mid),querysum(ch[x][1],mid+1,r)); } int rt[MAXN]; void dfs(int u,int f,int v) {modify(rt[u],1,w,1,w,data(0,1,0,0));modify(rt[u],1,w,1,val[u],data(v,0,0,0));for (int i=0;i<(int)e[u].size();i++)if (e[u][i]!=f){dfs(e[u][i],u,v);rt[u]=merge(rt[u],rt[e[u][i]]);}modify(rt[u],1,w,1,w,data(1,1,1,0)); } int calc(int v) {clear();for (int i=1;i<=n;i++) rt[i]=0;dfs(1,0,v);return querysum(rt[1],1,w); } int ans[MAXN],s[MAXN],tmp[MAXN],f[MAXN]; int main() {n=read(),k=read(),w=read();for (int i=1;i<=n;i++) val[i]=read();for (int i=1;i<n;i++){int u,v;u=read(),v=read();e[u].push_back(v),e[v].push_back(u);}for (int v=1;v<=n+1;v++)ans[v]=calc(v);s[0]=1;for (int i=1;i<=n+1;i++){for (int j=i;j>=1;j--)s[j]=dec(s[j-1],(ll)i*s[j]%MOD); s[0]=(ll)s[0]*(MOD-i)%MOD;}for (int i=1;i<=n+1;i++){int x=ans[i];for (int j=1;j<=n+1;j++) if (i!=j) x=(ll)x*qpow(dec(i,j),MOD-2)%MOD;tmp[0]=(ll)s[0]*qpow(MOD-i,MOD-2)%MOD;for (int j=1;j<=n+1;j++) tmp[j]=(ll)dec(tmp[j-1],s[j])*qpow(i,MOD-2)%MOD;for (int j=0;j<=n+1;j++) f[j]=(f[j]+(ll)x*tmp[j])%MOD;}int res=0;for (int i=k;i<=n;i++) res=add(res,f[i]);cout<<res;return 0; }

    總結

    以上是生活随笔為你收集整理的【九省联考2018】秘密袭击【树形dp】【生成函数】【线段树合并】【多项式插值】的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。