題目大意:
welcome to sao ( strange and abnormal online)。這是乙個 vr mmorpg, 含有 n 個關卡。但是,挑戰不同關卡的順序是乙個很大的問題。
有 n – 1 個對於挑戰關卡的限制,諸如第 i 個關卡必須在第 j 個關卡前挑戰, 或者完成了第 k 個關卡才能挑戰第 l 個關卡。並且,如果不考慮限制的方向性, 那麼在這 n – 1 個限制的情況下,任何兩個關卡都存在某種程度的關聯性。即, 我們不能把所有關卡分成兩個非空且不相交的子集,使得這兩個子集之間沒有任 何限制。
對於每個資料,輸出一行乙個整數,為攻克關卡的順序方案個數,mod 1,000,000,007 輸出。
題目翻譯:
發現最後一句話就是說:這是一棵樹形圖。
所以我們現在有了一棵樹,只是邊是有向邊,挑戰的限制就是邊的方向,我們必須把所有指向x0的關卡全部通過,才能通過x0關卡。
其實,所有指向x0的邊就是它的度數,所以可以看出來,
這個題是讓我們求這個樹形圖有多少種拓撲序。
分析:
這個題即使看了題解也是理解了半天。網上題解也不是很多,做法類似。
樹形計數問題,可以用樹形dp,首先我們可以先嘗試定義一維,定義f[i]表示以i為根的子樹的拓撲序有多少種,現在我們需要考慮怎樣將若干個兒子的值轉移到父親上。
發現,如果把兩個兒子的拓撲序,看做是兩個區間,那麼我們做的其實是乙個區間合併的操作。
但是由於邊其實是有向的,(雖然我們是無向邊建樹)實際邊的方向還決定父親,該兒子的真正完全的拓撲序誰在前,誰在後。就是說,要先過了父親,還是先過了兒子。
非常無從下手的感覺。我們需要再定義一維。
於是我們這樣定義:
f[i][j]表示,在以i為根的子樹中,根節點i排在第j位的拓撲序的種類數。(其實所有的拓撲序就是f[i][1-size])
可以發現,j不同時,方案數一定是獨立的。
現在我們要考慮轉移:
當我們迴圈到x的乙個兒子y的時候,size[x]記錄的是當前x與其前面所有兒子子樹的size和,就是還沒有包括y
那麼前面說了,就是乙個區間合併,我們以x的位置作為斷點考慮合併。
先分類(因為我們無向邊建樹,但是實際上是有向邊。)
①x
這個時候,拓撲序合併後x的排名一定在y的前面。
對於f[x][k],最終x前面有k-1個元素。可以從f[x][i](1<=i<=min(k,size))和 f[y][j](j的範圍隨後再確定)轉移過來。轉移之後,區間內共有size[x]+size[y]個數
就是說,我在合併後的拓撲序中,先從之前的f[x][i]中的方案數中拿出若干種,放進大區間裡,再從f[y][j]裡選擇一些方案數,放進大區間裡。所以這裡i一定小於等於k
前k-1個位置,從之前的數中先挑出i-1個位置,有c(k-1,i-1)種選法,
後size[x]+size[y]-k個位置(不算x), 已經選擇了i-1個數,還剩下size[x]-i個數(x自己不算),有c(size[x]+size[y]-k,size[x]-i)種選法。
再乘上每個選上的集合中自己的變化,也就是f[x][i]自己本身(類似多重集合的排列)
剩下的位置就是f[y][j]的了,不需要再乘組合數,只需乘上f[y][j]就好。
現在我們要確定j的取值範圍:
對於x為了使得y在x的後面,而y之前還能放j-1個數,所以要使得:j-1>=k-i,當然j<=size[y]
所以,j的迴圈範圍是,k-i+1<=j<=size[y]
所以,對於xf[x][k]=(1<=i<=min(k,size[x]))(k-i+1<=j<=size[y]) f[x][i]*c[k-1][i-1]*c[size[x]+size[y]-k][size[x]-i]*f[y][j]
這樣子,發現每次要迴圈一遍j,複雜度是o(n^4)的,直接掛掉。。。
又發現,對於同乙個y,我們好像加的是同一些樹,迴圈的是同一些j。。。
我們把這個式子用乘法分配律提出來一下:
f[x][k]=(1<=i<=min(k,size[x])) f[x][i]*c[k-1][i-1]*c[size[x]+size[y]-k][size[x]-i]*(f[y][k-i+1]+...f[y][size[y]])
所以,加粗部分是可以通過乙個字首合優化處理的,複雜度變成o(1)。
①x>y 即先通過y,再通過x。
其實是同理的。f[x][i](1<=i<=min(k,size)),i的範圍沒有變。
但是由於要保證y在x的前面,j-1個元素,必然不能填滿k-i個位置
所以,j-1=1
所以這裡的狀態轉移方程是:
f[x][k]=(1<=i<=min(k,size[x]))(1<=j<=k-i) f[x][i]*c[k-1][i-1]*c[size[x]+size[y]-k][size[x]-i]*f[y][j]
同理可以乘法分配律,字首和優化。
詳見**:
#include#define ull unsigned long long#define ll long long
using
namespace
std;
const
int n=1000+10
;const
int mod=1e9+7
;int
n,t;
struct
nodebian[
2*n];
inthead[n],cnt;
void add(int x,int y,int
z)ull f[n][n],sumdp[n][n];
ull c[n][n];
intsize[n];
bool
vis[n];
void dfs(int
x)
}f[x][k]=sum;}}
else
//.........y...x
f[x][k]=sum;
}}
size[x]+=size[y];}}
for(int i=1;i<=size[x];i++)//
處理完了x,賦值字首和,以便後續使用
sumdp[x][i]=(sumdp[x][i-1]+f[x][i])%mod;
}void clear()//
清空 }
intmain()
//1000的範圍,組合數打表
cin>>t;
while
(t)
else
//建無向邊,x,y距離是1,表示x}
dfs(1);
ull ans=0
;
for(int i=1;i<=size[1];i++)
//方案數
printf("
%llu\n
",ans);
t--;
}return0;
}
基本思路和**參考shadowice1984,
詳細化了很多。
NOIP 2013 計數問題
題目描述 試計算在區間1到n的所有整數中,數字x 0 x 9 共出現了多少次?例如,在1到11中,即在1 2 3 4 5 6 7 8 9 10 11中,數字1出現了4次。輸入每組輸入資料共1行,包含2個整數n x,之間用乙個空格隔開。資料規模 對於100 的資料,1 n 1,000,000,0 x ...
計數問題(二)
計數問題 二 在上一講中,我們一起研究 列舉法 乘法原理 加法原理 在計數問題中的應用。但是,在實際的問題中,這些方法並不是單獨使用的。往往需要同時應用這幾種方法,這就需要我們搞清題意,根據已知條件,分別使用正確的方法,得到準確的結果。一 閱讀思考 例1.求720這個數約數的個數。分析與解 從1開始...
問題 E 計數問題
時間限制 1 sec 記憶體限制 128 mb 提交 30 解決 22 提交狀態 討論版 命題人 admin 題目描述 試計算在區間 1 到 n 的所有整數中,數字 x 0 x 9 共出現了多少次?例如,在 1 到 11 中,即在 1 2 3 4 5 6 7 8 9 10 11 中,數字 1 出現了...