HEOI2013 SAO 計數問題

2022-02-06 05:09:04 字數 3597 閱讀 9697

題目大意:

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 出現了...