ZJOI2017 仙人掌 題解

2021-07-29 11:51:09 字數 1916 閱讀 3147

給出乙個無重邊無自環的無向連通圖(n 個點 m 條邊),問有多少種再往上加邊的方案,使得新圖是仙人掌。

多組資料, n<=5e5, ∑m

先要判斷讀入的圖是否是仙人掌。

部分分有樹,就先想樹怎麼做。

很直觀地設 f[i] 表示 i 為根的子樹的方案數,g[i] 表示有一條路要往上走的方案數。

不考慮根的匹配的話,那就是兒子的 f 或 g 乘起來,並且保證 g 有偶數個,再乘上偶數個兩兩匹配的方案數。

再考慮根的匹配,相當於列舉乙個兒子,然後乘上其他兒子的方案數。

但是這樣做不到線性。因為仙人掌不能含有重邊(兒子的根不能和父親相連),因此在轉移的時候,我們總是需要得出乙個對所有兒子的值,然後再列舉去掉乙個兒子之後的值,而前乙個做不到 o(1),因此後乙個也不可能 o(n)。

這樣暴力做是 o(n^2) 的,據說 fft 可以 log^2??

然後就需要乙個轉化。

我們把最後的非環邊強行看作兩條重邊,這樣轉化之後相當於每個點都必須要有一條延伸出去的邊,也就是去掉了不能有重邊的限制。(妙啊。。。)

然後這樣的 dp 就比之前的好轉移了。f 就是兒子的 g 乘起來,再乘個兩兩匹配的方案數,g 就是 f 再加上選乙個兒子出來然後其他兒子任意的方案數。(不懂請看**)

(這時的模型也可以看成是:選若干路徑覆蓋所有樹邊,的方案數)

如果讀入的圖是個仙人掌,那只要把環邊全部斷掉就是森林了。因為環邊不能參與任何運算,所以去掉是沒問題的。

(妙啊。。。×2)

#include

#include

#define fo(i,a,b) for(int i=a;i<=b;i++)

#define fd(i,a,b) for(int i=a;i>=b;i--)

using namespace std;

typedef long long ll;

const int maxn=5e5+5, maxm=1e6+5;

const ll mo=998244353;

int n,m;

int readint()

while (ch>='0' && ch<='9');

return data;

}int tot,fro[2

*maxm],go[2

*maxm],next[2

*maxm],f1[maxn];

bool bt[2

*maxm];

void ins(int

x,int

y)int dfn[maxn],low[maxn],sum,z[maxn],z0,rt[maxn];

bool bz[maxn];

bool tarjan(int k,int

last)

} else}}

if (dfn[k]==low[k])

return1;}

ll f[maxn],g[maxn],f[maxn];

void dfs(int k,int

last)

f[k]=f[num]*sum

%mo;

g[k]=(num==0) ?1 :(f[k]+sum*f[num-1]%mo

*num)%mo ;

}int t;

int main()

sum=0;

if (!tarjan(1,0))

fo(p,1,tot) if (rt[fro[p]]==rt[go[p]]) bt[p]=0;

ll ans=1;

fo(i,1,n) if (rt[i])

printf("%lld\n",ans);}}

ZJOI2017 仙人掌 轉化模型後的簡單樹形dp

給定乙個 n 個點,m條變的無向無自環的連通圖,問都多少種加邊方案使得加完邊的圖是一幅沒有重邊仙人掌。即滿足任意一條邊只屬於乙個簡單環中的無向無自環圖的連通圖 多組資料。n 5 10 5 m 106 首先討論給定的圖是樹的情況。化一 要求不能有邊存在與兩個簡單環內相當於我們要加入新的邊去覆蓋這個樹,...

題解 仙人掌計數

題目傳送門 給出 q 個查詢,每次查詢 n 個點的無根有標號仙人掌有多少個。q le 5 times 10 4,n 131072 因為這道題太難碼了,所以先把題解寫了再寫 好奇怪啊 終於碼出來了,果然還是 text 好用 霧 為了方便,我們下面的答案其實求的是有根有標號的答案,最後除以 n 就好了。...

清華集訓2015 靜態仙人掌 仙人掌剖分

毒瘤仙人掌,明明放到樹上一道板題的非要構造到仙人掌上來出題orz orz orz orz orz 在oi的上古時代,流傳著這樣乙個故事 有一天,小w到森林裡遊玩,回來之後跟小v說,我發現好多棵會動的樹耶!小v說,這有什麼好稀奇的,我用手指頭就能維護每棵樹的形態。於是又過了幾天小w到沙漠裡遊玩,回來之...