1. 程式人生 > 其它 >NOIP模擬44

NOIP模擬44

T1:

  考場轉化為數學語言,於是對引數解不等式,判斷是否存在整數解

時間複雜度O(Tnk),考慮區間操作可以通過線段樹維護,區間減除。然

而複雜度瓶頸在於k。

  正解為首先簡化問題,將黑條向前延伸s,顯然等價,考慮由於每次

走步長度固定,於是對於每個黑/白條,其出發區間一定,於是判斷可行

性可以轉化為判斷出發點是否存在交集,注意到關鍵決策點為黑條,判

斷黑條出發點區間之並是否能完全覆蓋k即可。事實上判斷白條出發點集

合是否有交也可做,然而白條並非關鍵決策點,即白條並不一定全部經過

需要判斷至少需要幾步才能通過,判斷區間之交是否滿足即可。

  注意取模改變數學系(取模後取區間並需要去重),變數名見名知意

程式碼如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I long long
 4 #define C char
 5 #define B bool
 6 const I N = 5e5 + 3;
 7 I T,s,k,n,l[N],r[N];
 8 struct S { I l,r; } ele[N << 1];
 9 struct Q { I l,r; } rea[N << 1];
10 inline I read() {
11     I x(0),y(1); C z(getchar());
12 while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); } 13 while ( isdigit(z)) x = x * 10 + (z ^ 48), z = getchar(); 14 return x * y; 15 } 16 inline B com (const S &a,const S &b) { 17 return a.l == b.l ? a.r > b.r : a.l < b.l; 18 } 19 signed main () { 20 T = read();
21 while (T -- ) { 22 B jud(0); I tmp(0),tot(0),cnt(0); 23 s = read(), k = read(), n = read(); 24 for (I i(1);i <= n; ++ i) { 25 tmp += read(); l[i] = r[i - 1] + 1; 26 r[i] = i & 1 ? tmp + s - 1 : tmp; 27 if (i & 1) { 28 if (r[i] - l[i] + 1 >= k) jud = 1; 29 I tmp1(l[i] % k + 1), tmp2(r[i] % k + 1); 30 if (tmp1 <= tmp2) ele[++tot] = (S){tmp1,tmp2}; 31 else ele[++tot] = (S){1,tmp2}, ele[++tot] = (S){tmp1,k}; 32 } 33 } 34 if (jud) { printf ("NIE\n"); continue; } 35 sort (ele + 1,ele + tot + 1,com); 36 I p1(1),p2(2); 37 while (p1 <= tot) { 38 rea[++cnt] = (Q){ele[p1].l,ele[p1].r}; 39 while (ele[p2].l >= ele[p1].l && ele[p2].r <= ele[p1].r && p2 <= tot) p2 ++ ; p1 = p2 ++ ; 40 } 41 for (I i(1);i <= cnt; ++ i) if (rea[i].l > rea[i - 1].r + 1) { jud = 1; break; } 42 if (rea[cnt].r < k) jud = 1; 43 jud ? printf ("TAK\n") : printf ("NIE\n"); 44 } 45 }
View Code

T2:

  理解並不深刻,三維(狀態空間)DP,由於各引數範圍很小(可以亂

搞)考慮定義DP:f[l][r][p][c]為之考慮區間l~r第p位以後的位置,並且欽定

第p位至少為c。

  狀態轉移:考慮三維空間下如何覆蓋l*r*p*c的體積,首先f[l][r][p][c]可

以由f[l][r][p][c+1]轉移而來,對應了l*r*p*(c-1)的體積,考慮如何轉移最後

一層,採用狀態劃分DP的思路,將f[l][r][p][c]劃分為f[l][i]與f[i + 1][r],由於

最後僅剩l*r*p*1(恰好為c的狀態),於是考慮欽定f[l][i][p]為c,為保證狀

態的合法性,必須滿足f[l][i][p + 1][0],否則可能存在不合法狀態,同理,

也必須滿足f[i + 1][r][p][c + 1]狀態

  注意判斷邊界,及時跳出即可

程式碼如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define I long long
 4 #define C char
 5 #define V void
 6 const I mod = 990804011;
 7 I n,u,f[55][55][25][30],A[55][25];
 8 C s[25];
 9 inline I max (I a,I b) { return a > b ? a : b; }
10 inline I read () {
11     I x(0),y(1); C z(getchar());
12     while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); }
13     while ( isdigit(z))  x = x * 10 + (z ^ 48), z = getchar();
14     return x * y;
15 }
16 I dfs (I l,I r,I p,I c) {
17     I &tmp(f[l][r][p][c]);
18     if ( ~ tmp) return tmp;
19     if (l >  r) return tmp = 1;
20     if (p >  u) return tmp = l == r;
21     if (c > 26) return tmp = 0;
22     tmp = dfs (l,r,p,c + 1);
23     for (I i(l);i <= r; ++ i) {
24       if (!(A[i][p] == c || (A[i][p] == 27 && c))) break;
25       tmp = (tmp + dfs (l,i,p + 1,0) * dfs (i + 1,r,p,c + 1)) % mod;
26     }
27     return tmp;
28 }
29 signed main () {
30     n = read();
31     memset (f,-1,sizeof f);
32     for (I i(1);i <= n; ++ i) {
33       scanf ("%s",s + 1);
34       u = max (u,strlen(s + 1));
35       for (I j(1),tmp(strlen(s + 1));j <= tmp; ++ j) 
36         A[i][j] = s[j] == '?' ? 27 : s[j] - 'a' + 1;
37     }
38     printf ("%lld\n",dfs (1,n,1,0));
39 }
View Code

T3:(口胡)

  考慮三種函式的數學本質,前兩種為直觀定義,考慮第三種的貢獻,

顯然只有當所有x均為1時才會作出1的貢獻,於是考慮第三種函式存在多

少種在經過若干次疊變後滿足所有x均為1。

  觀察第三種函式的轉化發現,對於每個x-1的情況其都完全考慮,也

就是說,第三種函式的疊變方式實質上是全排列,對於每個xi,將其疊變

為1需要疊變xi - 1次,由於疊變方式為全排列且每次只疊變1,於是考慮將

xi - 1視為xi - 1個1,現在對每組xi中的xi - 1個1全排列即可得到所有方案(

可以理解為賦予時間引數,對於1的排列代表在何時將xi減1),由於每組

xi中的1等價,於是問題就是可重集排列。

  現在考慮對於f函式的貢獻,即判斷其奇偶性,發現分母2因子個數必

定大於等於分子2因子個數,於是暴力思路為利用素因子篩法logn分別求

出分母分子2因子個數比較大小判斷奇偶性,然而這種求解方法到此結束

因為問題瓶頸在於優化搜尋樹,即如何高效判斷[l,r]區間的決策。

  於是考慮更改思路,嘗試通過分子分母本身的性質做到高效判斷,避

免冗餘轉移,於是利用庫默爾定理(詳見數學雜項),發現只需要判斷2進

制下,分子中是否存在進位即可,於是可以數位DP每組[l,r],使其儘量不

發生進位,對此數位DP即可