其實省選在四天前就已經結束了,但由於題目難度略大我到今天上午才補完所有題目……(捂臉逃)考場上很幸運,打完了所有我會寫的部分分,最后Round1的110分 + Round2的70分,勉強算是沒有被聯賽day2的文件夾坑太多。。。
目前網上比較容易找到的題解似乎只有ydc教主的這份:http://ydc.blog.uoj.ac/blog/336,不過對於我這種水平的人來說這份題解還不是很好理解,那么我再來稍微補充一點更基礎的內容吧。。
A.T1
有一棵點數為N的樹,樹邊有邊權。給你一個在0~N之內的正整數K,你要在這棵樹中選擇K個點,將其染成黑色,並將其他的N-K個點染成白色。將所有點染色后,你會獲得黑點兩兩之間的距離加上白點兩兩之間距離的和的收益。問收益最大值是多少。
其中$1 \leq N \leq 2000, 0 \leq K \leq N$。
分析.
從點和點的距離入手不太好分析……我在考場上想了一兩個小時最后還是棄療了,連暴力都沒心情寫,交了一份靠運氣的貪心騙分。
其實呢……由於樹上每一條邊都是橋,我們可以從每一條邊兩端的黑白點數入手,也就是考慮每條邊對答案造成的貢獻。定義函數f(v, i)表示在v子樹中,共將i個點染色后,能得到的“子樹中所有點的父邊的對答案的貢獻之和”的最大值。這樣每棵子樹就成了一個可分割的子結構,就可以做樹形背包了。
根據官方題解中給出的那種神奇的證明,這樣做的時間復雜度是$O(N^2)$.
代碼.
2 /* *********************By Asm.Def-Wu Jiaxin**************************** */
3 /* ********************************************************************* */
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 using namespace std;
11 #define SetFile(x) ( freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout) );
12 #define UseFREAD
13 #ifdef UseFREAD
14 #define getc() *(file_ptr++)
15 #define FreadLenth 5000000
16 char CHARPOOL[FreadLenth], *file_ptr = CHARPOOL;
17 #else
18 #define getc() getchar()
19 #endif
20 #ifdef DEBUG
21 #include
22 timeb SysTp;
23 #endif
24 template< class T>inline void getd(T &x){
25 char ch = getc(); bool neg = false;
26 while(!isdigit(ch) && ch != ' - ')ch = getc();
27 if(ch == ' - ')ch = getc(), neg = true;
28 x = ch - ' 0 ';
29 while(isdigit(ch = getc()))x = x * 10 - ' 0 ' + ch;
30 if(neg)x = -x;
31 }
32 /* ********************************************************************* */
33 const int maxn = 2003;
34 typedef long long LL;
35 #include
36 struct Edge{ int to, w;};
37 vector adj[maxn];
38
39 LL f[maxn][maxn], S[maxn][maxn];
40 int N, K, size[maxn];
41
42 inline void UPD(LL &a, const LL &b){ if(a < b)a = b;}
43
44 void dfs( int cur, int p, int w){
45 vector::iterator it;
46 int i, j, to;
47 size[cur] = 1;
48 for(it = adj[cur].begin();it != adj[cur].end();++it) if((to = (*it).to) != p){
49 dfs(to, cur, (*it).w);
50 for(i = min(size[cur], K);i >= 0;--i) for(j = min(size[to], K - i);j >= 0;--j)
51 UPD(S[cur][i + j], S[cur][i] + f[to][j]);
52 size[cur] += size[to];
53 }
54 for(i = size[cur];i >= 0;--i)
55 f[cur][i] = S[cur][i] + (LL)w * (i * (K - i) + (N - K - size[cur] + i) * (size[cur] - i));
56 }
57
58 inline void work(){
59 getd(N), getd(K);
60 int i, a, b, c;
61 for(i = 1;i < N;++i){
62 getd(a), getd(b), getd(c);
63 adj[a].push_back((Edge){b, c});
64 adj[b].push_back((Edge){a, c});
65 }
66 dfs( 1, 0, 0);
67 printf( " %lld\n ", f[ 1][K]);
68 }
69
70 int main(){
71
72 #ifdef DEBUG
73 freopen( " test.txt ", " r ", stdin);ftime(&SysTp);
74 size_t Begin_sec = SysTp.time, Begin_mill = SysTp.millitm;
75 #elif !defined ONLINE_JUDGE
76 SetFile(haoi2015_t1);
77 #endif
78
79 #ifdef UseFREAD
80 fread(file_ptr, 1, FreadLenth, stdin);
81 #endif
82
83 work();
84
85 #ifdef DEBUG
86 ftime(&SysTp);
87 printf( " \n%.3lf sec \n ", (SysTp.time - Begin_sec) + (SysTp.millitm - Begin_mill) / 1000.0);
88 #endif
89 return 0;
90 }
B.T2
有一棵點數為N的樹,以點1為根,且樹點有邊權。然后有M個操作,分為三種:
操作1 1 x a:把某個節點x的點權增加a。
操作2 2 x a:把某個節點x為根的子樹中所有點的點權都增加a。
操作3 3 x:詢問某個節點x到根的路徑中所有點的點權和。
對於100%的數據,N,M<=100000,且所有輸入數據的絕對值都不會超過10^6
分析.
其實我們這樣來看……對於一次操作(2 u x),我們來考慮這次操作對u的某個后代v的貢獻。不難看出,從u到v路徑上的每個點的權值都被增加了x,這個操作對v的答案的增量$d_v = (dist(u, v) + 1) * x$ 。然而dist(u, v)這個東西似乎不太容易作為區間修改的增量。由於u是v的祖先,我們不妨把dist拆一下,那么$d_v = (dep_v - dep_u + 1) * x$,或者$d_v = dep_v * x - (dep_u - 1) * x$.(其中$dep_i$表示i點到根的距離)這時我們其實已經把這次操作的貢獻拆成了一個關於點v的深度的一次函數,那么每次進行操作時只要分別修改兩項的系數就可以了。
代碼.
2 /* *****************************Designed By Asm.Def*************************** */
3 /* *************************************************************************** */
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 // #define FREAD
11 #ifdef FREAD
12 #define FREADLENTH 5000000
13 char *fread_ptr = ( char*) malloc(FREADLENTH);
14 #define getc() (*(fread_ptr++))
15 #else
16 #define getc() getchar()
17 #endif
18 #define SetFile(x) ( freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout) )
19 using namespace std;
20 template< class T>inline void getd(T &x){
21 int ch = getc(); bool neg = false;
22 while(!isdigit(ch) && ch != ' - ')ch = getc();
23 if(ch == ' - ')neg = true, ch = getc();
24 x = ch - ' 0 ';
25 while(isdigit(ch = getc()))x = x * 10 - ' 0 ' + ch;
26 if(neg)x = -x;
27 }
28 /* ***************************************************************************** */
29 const int maxn = 100005;
30 typedef long long LL;
31 #include
32 vector< int> adj[maxn];
33
34 int N, val[maxn], Begin[maxn], End[maxn], dep[maxn];
35
36 struct SegT{
37 int L, R, mid;
38 LL Sum, tg;
39 SegT *ls, *rs;
40 SegT( int l, int r):L(l), R(r), mid((l+r)>> 1){
41 Sum = tg = 0;
42 if(l == mid){
43 ls = rs = NULL;
44 return;
45 }
46 ls = new SegT(l, mid);rs = new SegT(mid, r);
47 }
48 inline void push(){Sum += tg * (R - L); if(ls)ls->tg += tg, rs->tg += tg;tg = 0;}
49 void Add( int l, int r, LL d){
50 if(l == L && r == R){
51 tg += d;push();
52 return;
53 }
54 push();
55 if(r <= mid)ls->Add(l, r, d);
56 else if(l >= mid)rs->Add(l, r, d);
57 else ls->Add(l, mid, d), rs->Add(mid, r, d);
58 Sum = ls->Sum + rs->Sum;
59 }
60 LL Query( int i){
61 if(tg)push();
62 if(L == mid) return Sum;
63 if(i < mid) return ls->Query(i);
64 if(i >= mid) return rs->Query(i);
65 }
66 }*R1, *R2;
67
68 void dfs( int cur, int p){
69 static int Iter = 0;
70 Begin[cur] = Iter++;
71 vector< int>::iterator it;
72 for(it = adj[cur].begin();it != adj[cur].end();++it) if(*it != p){
73 dep[*it] = dep[cur] + 1;
74 dfs(*it, cur);
75 }
76 End[cur] = Iter;
77 }
78
79 inline void init(){
80 int i, a, b;
81 for(i = 1;i <= N;++i)getd(val[i]);
82 for(i = 1;i < N;++i){
83 getd(a), getd(b);
84 adj[a].push_back(b);
85 adj[b].push_back(a);
86 }
87 dfs( 1, 0);
88 R1 = new SegT( 0, N);R2 = new SegT( 0, N);
89 for(i = 1;i <= N;++i)R1->Add(Begin[i], End[i], val[i]);
90 }
91
92 inline void opt1(){
93 int x, d;getd(x), getd(d);
94 R1->Add(Begin[x], End[x], d);
95 }
96
97 inline void opt2(){
98 int x, d;getd(x), getd(d);
99 R1->Add(Begin[x], End[x], (LL)( 1 - dep[x]) * d);
100 R2->Add(Begin[x], End[x], d);
101 }
102
103 inline void query(){
104 int x;getd(x);
105 printf( " %lld\n ", R1->Query(Begin[x]) + R2->Query(Begin[x]) * dep[x]);
106 }
107
108 int main(){
109
110 #ifndef DEBUG
111 SetFile(haoi2015_t2);
112 #else
113 freopen( " test.txt ", " r ", stdin);
114 #endif
115 #ifdef FREAD
116 fread(fread_ptr, 1, FREADLENTH, stdin);
117 #endif
118 int M, opt;getd(N), getd(M);
119 init();
120 while(M--){
121 getd(opt);
122 if(opt == 1)opt1();
123 else if(opt == 2)opt2();
124 else query();
125 }
126
127 #ifdef DEBUG
128 printf( " \n%.3lf sec\n ", ( double)clock() / CLOCKS_PER_SEC);
129 #endif
130 return 0;
131 }
C.T3
有一個長度為N的數組,甲乙兩人在上面進行這樣一個游戲:
首先,數組上有一些格子是白的,有一些是黑的。然后兩人輪流進行操作。每次操作選擇一個白色的格子,假設它的下標為x。接着,選擇一個大小在1~n/x之間的整數k,然后將下標為x、2x、...、kx的格子都進行顏色翻轉。不能操作的人輸。
現在甲(先手)有一些詢問。每次他會給你一個數組的初始狀態,你要求出對於這種初始狀態他是否有必勝策略。假設兩人總能做出最優決策。
對於30%的數據,N<=20;
對於50%的數據,N<=1000000;
對於70%的數據,N<=10000000;
對於100%的數據,N<=1000000000,K,W<=100,不會有格子在同一次詢問中多次出現。
分析.
好難啊……TAT考場上果斷30分暴搜……
出題人kzf告訴我們,可以修改一下游戲的定義:每一步可以選擇黑格子或白格子,最終能將所有格子都翻成黑色的人贏。由於每次翻轉黑格子的操作都不可能直接帶來勝利,而任何一步選取黑格子進行的有利操作都能由對方通過再翻一次這個格子抵消掉,根據假設“兩個人都是足夠聰明的”,我們知道游戲中一定不會有人選擇黑格子。這就保證了這種轉換的正確性。
因此我們可以把每個黑色的位置都看成偶數個白格子疊加起來得到的,並將每個位置當做一個獨立的游戲來計算SG函數,就可以套用常規的博弈題思路了。根據游戲規則,我們有$$SG(i) = \mathop{mex} \limits_{1 \leq k \leq N / i} \{ \mathop{\oplus } \limits_{2 \leq t \leq k} \{ SG(t*i) \}\}$$(其中mex(S)表示S中沒有出現的最小自然數)按定義遞推就可以拿到50分了。
考慮到當一個點i走到N要跳的步數一定時,我們可以把這些i的倍數的序列映射到另一個序列2...N/i,這樣我們就可以用數學歸納證明每個點i的SG值只與從它跳到N需要的次數(即 N / i)有關。然后我們可以注意到,i從1到N中,N / i只會有O($\sqrt{N})$種取值,所以我們只需遞推$O(\sqrt{N})$輪就可以了。然而……在遞推時不論是維護一個指針線性遞推還是用二分查找結構在線查找都只能拿到70分……
然后出題人kzf又告訴我們,存在很多相鄰的按N/i划分的區間有着相同的SG值(原因大概是跳動次數增加時多跳的那一次改變的那些SG和不容易恰好等於增加之前的SG值?反正我不會證明……)。所以我們在預處理時只要判斷是否能和上一塊合並就可以了。復雜度……嗯……大概是O(玄學 × $N$)。不過實際跑起來還是挺快的……
Update: 考慮上面說的70分算法,在遞推的過程中我們需要盡可能快地查詢以前求出的SG值,那么我們直接用一個哈希表就可以解決問題了!(我以前還沒寫過哈希表……太弱啦……)時間復雜度大概是$T(N) = \sum_{i=1}^{\sqrt{N}} (\sqrt{\frac{N}{i}} + \sqrt{i} ) × \frac{N}{P}$,這個求出來大概是$O(\frac{N^{\frac{7}{4}}}{P})$,總算是不用玄學完美地解決了= =
代碼.
2 /* *****************************Designed By Asm.Def*************************** */
3 /* *************************************************************************** */
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #define SetFile(x) (freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout) )
11 // #define FREAD
12 #define FREADLENTH 5000000
13 #ifdef FREAD
14 char *fread_ptr = ( char*) malloc(FREADLENTH);
15 #define getc() (*(fread_ptr++))
16 #else
17 #define getc() getchar()
18 #endif
19 using namespace std;
20 template< class T>inline void getd(T &x){
21 int ch = getc(); bool neg = false;
22 while(!isdigit(ch) && ch != ' - ')ch = getc();
23 if(ch == ' - ')neg = true, ch = getc();
24 x = ch - ' 0 ';
25 while(isdigit(ch = getc()))x = x * 10 - ' 0 ' + ch;
26 if(neg)x = -x;
27 }
28 /* ***************************************************************************** */
29 const int maxn = 64000; // SG[len]
30 int N, val[maxn], vcnt, vt[maxn], checklist[maxn];
31 bool check[maxn];
32 int SG[maxn], id[maxn], idcnt;
33 #define ind(x) ( upper_bound(id, id + idcnt, x) - id - 1 )
34 inline void init(){
35 getd(N);
36 int i, j, t, s, it, vtcnt, c, tmp, lastSG = 0;
37 int ccnt; // 回滾check數組
38 for(i = 1;i * i <= N;++i)val[vcnt++] = i;
39 for(j = N / i;j;--j)val[vcnt++] = N / j;
40 for(i = 0;i < vcnt;++i){
41 t = val[i];s = ccnt = vtcnt = 0;
42 for(j = 1;j * j <= t;++j)vt[vtcnt++] = j;
43 for(j = t / j;j;--j)vt[vtcnt++] = t / j; // vt:能跳到的位置的N/i的所有取值
44 for(it = idcnt- 1, j = vtcnt- 2;j >= 0;--j){
45 while(id[it] > vt[j])--it;
46 tmp = SG[it];
47 c = t / vt[j] - t / (vt[j] + 1); // 出現次數
48 check[checklist[ccnt++] = s ^ tmp] = true;
49 if(c & 1)s ^= tmp;
50 }
51 j = 1; while(check[j])++j;
52 id[idcnt] = t;
53 if(j != lastSG)lastSG = SG[idcnt++] = j;
54 for(j = 0;j < ccnt;++j)check[checklist[j]] = false;
55 }
56 }
57
58 inline void work(){
59 int K, qcnt, sum, t;
60 getd(K); while(K--){
61 getd(qcnt);
62 sum = 0;
63 while(qcnt--){
64 getd(t);
65 sum ^= SG[ind(N / t)];
66 }
67 if(sum)puts( " Yes ");
68 else puts( " No ");
69 }
70 }
71
72 int main(){
73
74 #ifdef DEBUG
75 freopen( " test.txt ", " r ", stdin);
76 #else
77 SetFile(haoi2015_t3);
78 #endif
79 #ifdef FREAD
80 fread(fread_ptr, 1, FREADLENTH, stdin);
81 #endif
82 init();
83 work();
84
85 #ifdef DEBUG
86 printf( " \n%.3lf sec\n ", ( double)clock() / CLOCKS_PER_SEC);
87 #endif
88 return 0;
89 }
2 /* *****************************Designed By Asm.Def*************************** */
3 /* *************************************************************************** */
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #define SetFile(x) (freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout) )
11 // #define FREAD
12 #define FREADLENTH 5000000
13 #ifdef FREAD
14 char *fread_ptr = ( char*) malloc(FREADLENTH);
15 #define getc() (*(fread_ptr++))
16 #else
17 #define getc() getchar()
18 #endif
19 using namespace std;
20 template< class T>inline void getd(T &x){
21 int ch = getc(); bool neg = false;
22 while(!isdigit(ch) && ch != ' - ')ch = getc();
23 if(ch == ' - ')neg = true, ch = getc();
24 x = ch - ' 0 ';
25 while(isdigit(ch = getc()))x = x * 10 - ' 0 ' + ch;
26 if(neg)x = -x;
27 }
28 /* ***************************************************************************** */
29 const int maxn = 64000, P = 3500017;
30 int N, val[maxn], vcnt, vt[maxn], checklist[maxn];
31 bool check[maxn];
32 int SG[maxn];
33
34 int First[P], Next[P];
35
36 inline int ind( int x){
37 int t = First[x % P];
38 while(t && val[t] != x)t = Next[t];
39 return t;
40 }
41
42 inline void push( int x, int ind){
43 Next[ind] = First[x];
44 First[x] = ind;
45 }
46
47 inline void init(){
48 getd(N);
49 int i, j, t, s, vtcnt, c, tmp;
50 int ccnt; // 鍥炴粴check鏁扮粍
51 for(i = 1;i * i <= N;++i)val[++vcnt] = i;
52 for(j = N / i;j;--j)val[++vcnt] = N / j;
53 for(i = 1;i <= vcnt;++i){
54 t = val[i];s = ccnt = vtcnt = 0;
55 for(j = 1;j * j <= t;++j)vt[vtcnt++] = j;
56 for(j = t / j;j;--j)vt[vtcnt++] = t / j; // vt:涔榢涔嬪悗鑳藉嚭鐜扮殑闀垮害
57 for(j = vtcnt- 2;j >= 0;--j){
58 tmp = SG[ind(vt[j])];
59 c = t / vt[j] - t / (vt[j] + 1); // 鍑虹幇嬈℃暟
60 check[checklist[ccnt++] = s ^ tmp] = true;
61 if(c & 1)s ^= tmp;
62 }
63 j = 1; while(check[j])++j;
64 SG[i] = j;
65 push(t % P, i);
66 for(j = 0;j < ccnt;++j)check[checklist[j]] = false;
67 }
68 }
69
70 inline void work(){
71 int K, qcnt, sum, t;
72 getd(K); while(K--){
73 getd(qcnt);
74 sum = 0;
75 while(qcnt--){
76 getd(t);
77 sum ^= SG[ind(N / t)];
78 }
79 if(sum)puts( " Yes ");
80 else puts( " No ");
81 }
82 }
83
84 int main(){
85
86 #ifdef DEBUG
87 freopen( " test.txt ", " r ", stdin);
88 #else
89 SetFile(haoi2015_t3);
90 #endif
91 #ifdef FREAD
92 fread(fread_ptr, 1, FREADLENTH, stdin);
93 #endif
94 init();
95 work();
96
97 #ifdef DEBUG
98 printf( " \n%.3lf sec\n ", ( double)clock() / CLOCKS_PER_SEC);
99 #endif
100 return 0;
101 }
D.set
剛開始你有一個數字0,每一秒鍾你會隨機選擇一個[0,2^n-1]的數字,與你手上的數字進行按位或(C和C++的|,Pascal的or)操作。
選擇數字i的概率是p[i]。保證0<=p[i]<=1,∑p[i]=1
問期望多少秒后,你手上的數字變成2^n-1。
對於30%的數據,n<=10
對於60%的數據,n<=15
對於100%的數據,n<=20
分析.
考場上我寫了個錯誤的遞推,不過我概率論學得渣不知道為什么不對,總之我在考場上嘗試了很久都跑不出樣例,最終直接輸出個INF就放那了= =
換個思路,我們嘗試用常規的期望的定義來求。設函數$f_k (S) $為第k輪集合為S的概率,那么答案就是
$$\sum_{i=1}^{\infty} i * (f_i (U) - f_{i-1} (U) )$$
不難寫出函數f的遞推式:
$$f_{i+1}(S) = \sum_a \sum_b [a \cup b = S] f_{i}(a) * P(b)$$
不過這個式子看上去不太好轉移對吧?我們可以給它加個特技,不要管它們的並集是誰了,我們直接來考慮等式兩邊S集合的所有子集的函數值之和(這步變換可以通過集合論中的莫比烏斯反演來逆轉),即:
$$\sum_{s' \subseteq S} f_{i+1}(s') = \sum_{a \subseteq S} \sum_{b \subseteq S} f_i(a) * P(b)$$
或$$\sum_{s' \subseteq S} f_{i+1}(s') = (\sum_{a \subseteq S} f_i(a) ) (\sum_{b \subseteq S} P(b))$$
發現了什么嗎?某一項在一個集合上的答案的子集和恰好等於前一項上同一集合的子集和與這一集合的初始概率函數(也就是f_1函數)的子集和的乘積。換句話說,如果我們設$F_i (S) = \sum_{s' \subseteq S} f_i (s')$,那么我們的轉移方程就可以寫成$F_{i+1}(S) = F_i (S) * F_1 (S) $。瞬間變得非常簡單有木有!!!什么求和都沒有了,我們立刻就可以得到F的通項公式:$F_i (S) = (F_1 (S)) ^ i$.
那么現在我們的$F_i$函數就已知了,我們來看看怎么由$F_i$函數推出$f_i$。我們不妨令$f_i (S) = \sum_{S' \subseteq S} \mu(S', S) F_i(S')$,其中$\mu(S', S)$是我們要求的莫比烏斯函數(注意這里指的是集合論中廣義的莫比烏斯反演)。將F代入,可得
$$f_i(S) = \sum_{S' \subseteq S} \mu(S', S) \sum_{S'' \subseteq S'} f_i(S'')$$ 由容斥原理可以得出,$\mu(S', S) = (-1)^{|S| - |S'|}$。從而我們可以得到:$$f_i (S) = \sum_{S' \subseteq S} (-1)^{|S| - |S'|} F_i(S')$$.代入$F_i$(S')的通項公式,可得:$$f_i(S) = \sum_{S' \subseteq S} (-1)^{|S| - |S'|} (F_1 (S')) ^ i)$$
那么我們的最終答案就可以表示為
由於F函數不會超過1,可直接利用冪級數求和得出答案:$$ \sum_{S' \subseteq S} (-1)^{|S| - |S'|} * ( \frac{1}{F_1(S') - 1} ) $$
而計算一個函數的子集和函數可以用標程中給出的代碼用$O(n2^n)$的時間復雜度求出。這樣,這道題就完美解決啦!
代碼.
2 /* *********************By Asm.Def-Wu Jiaxin**************************** */
3 /* ********************************************************************* */
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 using namespace std;
11 #define SetFile(x) ( freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout) );
12 // #define UseFREAD
13 #ifdef UseFREAD
14 #define getc() *(file_ptr++)
15 #define FreadLenth 5000000
16 char CHARPOOL[FreadLenth], *file_ptr = CHARPOOL;
17 #else
18 #define getc() getchar()
19 #endif
20 #ifdef DEBUG
21 #include
22 timeb SysTp;
23 #endif
24 template< class T>inline void getd(T &x){
25 char ch = getc(); bool neg = false;
26 while(!isdigit(ch) && ch != ' - ')ch = getc();
27 if(ch == ' - ')ch = getc(), neg = true;
28 x = ch - ' 0 ';
29 while(isdigit(ch = getc()))x = x * 10 - ' 0 ' + ch;
30 if(neg)x = -x;
31 }
32 /* ********************************************************************* */
33 const int maxn = 1 << 20;
34 const double eps = 1e- 8;
35 double P[maxn];
36 int n, N;
37 inline double m_abs( double x) { return x < 0 ? -x : x;}
38 inline void work(){
39 getd(n);N = 1 << n;
40 int i, j, t, tmp;
41
42 for(i = 0;i < N;++i){
43 scanf( " %lf ", P + i);
44 if(P[i])tmp |= i;
45 }
46 if(tmp+ 1 != N){
47 puts( " INF ");
48 return;
49 }
50
51 for(t = 1;t < N;t <<= 1) // Modulate
52 for(j = 0;j < N;++j) if(j & t)P[j] += P[j ^ t];
53
54 for(j = 0;j < N;++j){
55 if(m_abs(P[j] - 1.0) < eps)P[j] = 0;
56 else P[j] = 1.0 / (P[j] - 1.0);
57 }
58
59 for(t = 1;t < N;t <<= 1) // Demodulate
60 for(j = 0;j < N;++j) if(j & t)P[j] -= P[j ^ t];
61
62 printf( " %.10lf\n ", P[N - 1]);
63 }
64
65 int main(){
66
67 #ifdef DEBUG
68 freopen( " test.txt ", " r ", stdin);ftime(&SysTp);
69 size_t Begin_sec = SysTp.time, Begin_mill = SysTp.millitm;
70 #elif !defined ONLINE_JUDGE
71 SetFile(haoi2015_set);
72 #endif
73
74 #ifdef UseFREAD
75 fread(file_ptr, 1, FreadLenth, stdin);
76 #endif
77
78 work();
79
80 #ifdef DEBUG
81 ftime(&SysTp);
82 printf( " \n%.3lf sec \n ", (SysTp.time - Begin_sec) + (SysTp.millitm - Begin_mill) / 1000.0);
83 #endif
84 return 0;
85 }
E.str
你有一個長度為n的數字串。
定義f(S)為將S拆分成若干個1~m的數的和的方案數,比如m=2時,f(4)=5,分別為4=1+1+1+1, 4=1+1+2, 4=1+2+1, 4=2+1+1, 4=2+2
你可以將這個數字串分割成若干個數字(允許前導0),將它們加起來,求f,並求和。
比如g(123)=f(1+2+3)+f(1+23)+f(12+3)+f(123)。
已知字符串和m后求答案對998244353(7*17*223+1,一個質數)取模后的值。
對於30%的數據,字符串長度不超過5
對於60%的數據,字符串長度不超過18
對於100%的數據,字符串長度不超過500,m<=5
分析.
不難看出,$f(i) = \sum_{j=1}^m f(i-j)$.很容易把這個遞推式寫成一個m × m的矩陣M。暫時撇開f函數前m項構成的向量$F_0$,我們考慮對這整個轉移矩陣做dp。設g(i)表示能夠將$F_0$轉移到$F_{num_{0, i}}$的轉移矩陣。其中$num_{i, j}$表示字符串的第i+1到j位構成的十進制數。
根據矩陣乘法對加法的分配律,可以得到$g(i) = \sum_{j = 0}^{i-1} g(j) * M ^ {num(j, i)}$,先用十進制矩陣快速冪的思路求出F的10的各冪次的冪,然后dp求出最終的矩陣,最后左乘向量求值即可。總復雜度$O({len}^2 m^3)$ 。值得注意的是……這題卡常數,矩陣乘法一定要盡量減少取模的次數。
2 /* *********************By Asm.Def-Wu Jiaxin**************************** */
3 /* ********************************************************************* */
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 using namespace std;
11 #define SetFile(x) ( freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout) );
12 #define UseFREAD
13 #ifdef UseFREAD
14 #define getc() *(file_ptr++)
15 #define FreadLenth 5000000
16 char CHARPOOL[FreadLenth], *file_ptr = CHARPOOL;
17 #else
18 #define getc() getchar()
19 #endif
20 #ifdef DEBUG
21 #include
22 timeb SysTp;
23 #endif
24 template< class T>inline void getd(T &x){
25 char ch = getc(); bool neg = false;
26 while(!isdigit(ch) && ch != ' - ')ch = getc();
27 if(ch == ' - ')ch = getc(), neg = true;
28 x = ch - ' 0 ';
29 while(isdigit(ch = getc()))x = x * 10 - ' 0 ' + ch;
30 if(neg)x = -x;
31 }
32 /* ********************************************************************* */
33 const int maxn = 505, mod = 998244353;
34 typedef long long LL;
35
36 int n, num[maxn], len, Vect[ 5];
37
38 struct Mat{ int A[ 5][ 5];}I, F, dp[maxn], Pow[ 10][maxn], Pow10[ 10];
39 inline Mat operator * ( const Mat &a, const Mat &b){
40 Mat ans;
41 int i, j, k;
42 unsigned long long tmps;
43 for(i = 0;i < n;++i) for(j = 0;j < n;++j){
44 tmps = 0;
45 for(k = 0;k < n;++k)tmps += (LL)a.A[i][k] * b.A[k][j];
46 ans.A[i][j] = tmps % mod;
47 }
48
49 return ans;
50 }
51
52 inline void operator += (Mat &a, const Mat &b){
53 int i, j;
54 for(i = 0;i < n;++i) for(j = 0;j < n;++j){
55 a.A[i][j] += b.A[i][j];
56 if(a.A[i][j] >= mod)a.A[i][j] -= mod;
57 }
58 }
59
60 inline Mat power(Mat a, int t){
61 Mat ans = I;
62 while(t){ if(t & 1)ans = ans * a;a = a * a;t >>= 1;}
63 return ans;
64 }
65
66 inline void init(){
67 int i, j, ch = getc(); while(!isdigit(ch))ch = getc();
68 while(isdigit(ch)){
69 num[len++] = ch - ' 0 ';
70 ch = getc();
71 }
72 getd(n);
73 *Vect = 1; for(i = 1;i < n;++i) for(j = 0;j < i;++j)Vect[i] += Vect[j];
74 for(i = 1;i < n;++i)F.A[i][i- 1] = 1;
75 for(i = 0;i < n;++i)F.A[i][n- 1] = 1;
76 for(i = 0;i < n;++i)I.A[i][i] = 1;
77 Pow10[ 0] = I;Pow10[ 1] = F;
78 for(i = 2;i < 10;++i)Pow10[i] = Pow10[i- 1] * F;
79 Mat tmp = I;
80 for(i = 1;i < 10;++i){
81 tmp = tmp * F;
82 Pow[i][ 1] = tmp;
83 for(j = 2;j <= len;++j)Pow[i][j] = power(Pow[i][j- 1], 10);
84 }
85 }
86
87 inline void work(){
88 int i, j, t;
89 Mat tmp;
90 dp[ 0] = I;
91 for(i = 1;i <= len;++i){
92 tmp = I;
93 for(j = 1;j <= i;++j){
94 if((t = num[i-j]))tmp = tmp * Pow[t][j];
95 dp[i] += dp[i - j] * tmp;
96 }
97 }
98 int Ans = 0;
99 for(i = 0;i < n;++i)Ans = ((LL)dp[len].A[i][ 0] * Vect[i] + Ans) % mod;
100 printf( " %d\n ", Ans);
101 }
102
103 int main(){
104
105 #ifdef DEBUG
106 freopen( " test.txt ", " r ", stdin);
107 #elif !defined ONLINE_JUDGE
108 SetFile(haoi2015_str);
109 #endif
110 #ifdef UseFREAD
111 fread(file_ptr, 1, FreadLenth, stdin);
112 #endif
113 init();
114 work();
115
116 #ifdef DEBUG
117 printf( " \n%.3lf sec \n ", ( double)clock() / CLOCKS_PER_SEC);
118 #endif
119 return 0;
120 }
