T1:降低的出現後,以後都填9,注意前綴0c++
const int maxN = 1e5 + 7; char s[maxN]; int main() { scanf("%s",s + 1); int n = strlen(s + 1); int now = s[1] - '0'; int pos = 1,tmp = 1; for(int i = 2;i <= n;++ i) { int x = s[i] - '0'; if(x > now) {now = x;pos = i;} else {if(x < now) break;} if(x == now) tmp = i; } if(tmp == n) { printf("%s",s + 1); return 0; } for(int i = 1;i < pos;++ i) { printf("%c",s[i]); } if(s[pos] != '1') printf("%c",s[pos] - 1); for(int i = pos + 1;i <= n;++ i) printf("9"); return 0; }
T2:考慮一個 逆序對 對多少個區間產生貢獻。
即答案爲\(\sum_{a_i < a_j,i > j} a_i * a_j * j * (n - i + 1)\%(10^{12} + 7)\)
直接用樹狀數組維護一下.
比較特殊的是模數太大會爆int.數組
const int mod = 1e12 + 7; const int maxN = 4e4 + 7; int a[maxN] , ans,p[100007],b[maxN],s[maxN]; int query(int x) { int ans = 0; for(;x;x -= x & -x) ans = (ans + p[x]) % mod; return ans; } const int MAX = 1e5; void add(int x , int val) { for(;x <= MAX;x += x & -x) { p[x] = (p[x] + val) % mod; } return ; } int mul(int a , int b) { int ans = 0; for(int now = a;b;b >>= 1, now = (now + now) % mod) { if(b & 1) ans = (ans + now) % mod; } return ans; } signed main() { freopen("multiplication.in","r",stdin); freopen("multiplication.out","w",stdout); int n = gi(); for(int i = 1;i <= n;++ i) s[i] = b[i] = gi(); sort(s + 1,s + n + 1); for(int i = 1;i <= n;++ i) a[i] = lower_bound(s + 1,s + n + 1,b[i]) - s; for(int i = 1;i <= n;++ i) { int tmp = query(n) - query(a[i]); tmp %= mod; tmp += mod; tmp %= mod; ans = ans + mul(mul(tmp , (n - i + 1)) , b[i]) % mod; ans %= mod; add(a[i] , b[i] * i); } ans %= mod; ans += mod; ans %= mod; cout << ans; return 0; }
T3:
我在考場裏的想法:優化
把矩陣縮小,二維離散化後,用二維樹狀數組維護。
單點加,區間查詢。
而後直接用掃描線求矩陣並。spa
學弟tzt沒有判斷矩陣交,直接判斷矩陣並 是否等於 最大的矩形就過了。
可能老師造數據的時候沒有卡掉這一點。調試
加入這個矩陣的邊時,直接check線段樹上的值是否爲0就能判交了。
因此大概A掉的作法應該是兩遍掃描線。code
gdb調試排序
T1:大大大大大大模擬遊戲
struct player { char name[16]; int num, score; } a[2][5][500100]; int n, m, t, p, q, turn[2][5], sum[2][5], psum, qsum, num, score, team, pos; char sname[16], spos[16]; bool cmp(player a, player b) { return (a.score == b.score ? a.num < b.num : a.score > b.score); } int main() { freopen("match.in", "r", stdin); freopen("match.out", "w", stdout); scanf("%d%d%d%d%d", &n, &m, &t, &p, &q); for (int i = 1; i <= n + m; i++) { team = (i <= n ? 0 : 1); scanf("%s %d %s %d", sname, &num, spos, &score); if (spos[0] == 'p' && spos[1] == 'g') pos = 0; if (spos[0] == 's' && spos[1] == 'g') pos = 1; if (spos[0] == 's' && spos[1] == 'f') pos = 2; if (spos[0] == 'p' && spos[1] == 'f') pos = 3; if (spos[0] == 'c') pos = 4; a[team][pos][++sum[team][pos]] = (player) {"", num, score}; strcpy(a[team][pos][sum[team][pos]].name, sname); } for (int i = 0; i <= 4; i++) { sort(a[0][i] + 1, a[0][i] + sum[0][i] + 1, cmp); sort(a[1][i] + 1, a[1][i] + sum[1][i] + 1, cmp); } for (int i = 0; i <= 4; i++) turn[0][i] = turn[1][i] = 1; psum = 1, qsum = 1; while (p * psum < t || q * qsum < t) { if (p * psum <= q * qsum) team = 0, psum++; else team = 1, qsum++; int mn = 999, pm; player up, down; for (int i = 0; i <= 4; i++) { int k = turn[team][i], c = a[team][i][k].score - a[team][i][k + 1].score; if (k == sum[team][i]) continue; if (c < mn || (c == mn && a[team][i][k + 1].num < up.num)) mn = c, pm = i, up = a[team][i][k + 1], down = a[team][i][k]; } turn[team][pm]++; char ct = (team == 0 ? 'A' : 'B'); printf("Substitution by %c,No.%d %s is coming up to change No.%d %s.\n", ct, up.num, up.name, down.num, down.name); } return 0; }
T2:挺神的一道題,題目難度偏大。
考場只想到了80\(n(log_2n)^2\)暴力作法
正解是二分+單調性。
二分在上面累計的格子,而後會發現這個東西實際上是單調的。ip
#include<bits/stdc++.h> #define N 500010 using namespace std; typedef long long ll; ll s[N], T; int x[N], a[N], n; bool check(ll H) { ll nowh = 0, tim = 0; int lc = 0, rc = 0, l = 1, r = n + 1; for (int i = 1; i <= n; i++) if (nowh + a[i] <= H) nowh += a[i], tim += (ll)(x[i] - x[1]) * a[i]; else {r = i, rc = H - nowh, tim += (ll)(x[i] - x[1]) * rc; break;} if (tim <= T) return 1; for (int i = 2; i <= n; i++) { ll suml = s[i - 1] - s[l - 1] - lc; ll sumr = s[r - 1] - s[i - 1] + rc; tim += (x[i] - x[i - 1]) * (suml - sumr); while (r <= n && x[i] - x[l] > x[r] - x[i]) { int can = min(a[l] - lc, a[r] - rc); tim += (x[r] - x[i] - x[i] + x[l]) * can; lc += can, rc += can; if (lc >= a[l]) ++l, lc = 0; if (rc >= a[r]) ++r, rc = 0; } if (tim <= T) return 1; } return 0; } int main() { freopen("block.in", "r", stdin); freopen("block.out", "w", stdout); scanf("%d%I64d", &n, &T); T /= 2; for (int i = 1; i <= n; i++) scanf("%d", &x[i]); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i]; ll l = 0, r = s[n], ans; while (l <= r) { ll mid = (l + r) >> 1; if (check(mid)) l = mid + 1, ans = mid; else r = mid - 1; } printf("%I64d\n", ans); return 0; }
T3:神仙manacher題不會作it
CF17E Palisection
首先補集轉化。轉而計算有多少個迴文子串不相交
首先處理全部以該點爲中心的迴文子串能擴展到的最大位置。
而後差分,前綴和。
再前綴和。
處理答案便可。
codeforces 1045B
分析問題的神仙題。
考慮一個數\(z\)怎麼才能不被表示出來。
就是對於每個\(a_i\)都能找到另外一個\(a_j\)使得\((a_i+a_j) % M = z\)
把序列a從小到大排序
若是\(a_i <= Z\),則\(a_j\)必定\(<= M\)
若是\(a_i > Z\),則\(a_j\)必定\(> M\)
這個是能夠證實的。
而後把序列分紅兩部分要保證兩部分首位相等,即迴文。
這樣就能夠處理迴文串,而後差分加上,最後用前綴和計算答案。
幾個簡單的小問題
數軸上有n個開區間\((a_i,b_i)\)。選擇儘可能多個區間,是的這些區間兩兩沒有公共點。
把b_i從小到大排序
必定選擇第一個,記錄l,不想交能選就選。
數軸上有n個閉區間\([a_i,b_i]\),取儘可能少的點,使得每一個區間至少有一個點。
按左端點遞增順序排序,若是左端點相同,按右端點遞增順序排序
記錄最右點,不行則新增長一個點。
給定n個按長度從大到小排序的區間,取若干個區間使得[1,M]能被覆蓋,並要使得最短的區間儘可能長。
複雜度要求比\(nlogn\)小
並查集,依次添加區間,f_i表示i能達到的最右邊的點,相似於並查集同樣維護起來。
添加區間的時候,直接開始跳,若是沒有被標記,則連起來。
USACO2005 dec sliver
在n個帶權區間中,選擇一些區間,覆蓋\([1,M]\)全部整數點,求權和的最小值。
\(n \le 10^5,m\le 10^9\)
按照y_i排序,設\(f_i\)表示覆蓋徹底\([1,y_i]\)的權值和的最小值
若\(y_j >= x_i\)則\(f_i = min(f_i,f_j + w_i)\)
用樹狀數組維護
codeforces 776C
一個長度爲n的序列,給定數k,求有多少個區間的和是\(k^i(i = 0,1,2,3,4,5....)\)
\(n \le 1e5, A_i \le 1e5\)
求一個前綴和,問題轉化爲\((sum_i - sum_j) \% k == 0\)的方案數
直接用\(f_i\)表示sum_j模k -= i的方案數。而後動態更新答案就好了。
xdoj1079
前綴和水題
bzoj1044
首先二分長度check出總長度最長的一塊最小K.
而後設\(f_{i,j}\)表示前i個木棍,
矩陣乘法
忘記矩陣怎麼推的了。
入門構造題: poj3735 Training little cats
P3758 [TJOI2017]可樂
矩陣優化DP
設\(f_{i,j}\)表示走了j步到i的方案數.
對於操做1\(f_{i,j} = f_{i,j - 1}\)
對於操做2\(f_{i,j} = f_{v,j - 1}\)
操做3就是對Ans累加這個和
而後咱們構造一個矩陣來優化快速冪,就是醬紫。
P3597 [POI2015]WYC
二分第k小路徑的長度,而後用矩陣快速冪計算方案數,check是否>= k
計算方案數的時候。拆點,對於長度爲2的邊,多拆出一個點,對於長度爲3的邊,多拆除兩個點
BZOJ2510 弱題
不會作.
P2044 [NOI2012]隨機數生成器
矩陣優化遞推。
3-sat
哈密爾頓路(迴路
三分圖
最大
若是一個狀態只能轉移必勝態,那這個是必敗態。
若是一個狀態轉移出去的有一個是必敗態,那麼這個狀態是必勝態。
這樣就能夠設狀態DP了.
題目:
有兩個數x,y = (1,0)。
每次有三種可能的操做。
1.(x,y) -> (1,x+y)
2.(x,y) -> (2x,y)
3.(x,y) -> (3x,y)
給定一個數K.
y >= k.他就輸,x + y >= k時,只能使用1操做
k<=5w.
首先考慮樸素的就是\(f_{i}{j}\)表示x=i,y = j是必勝態仍是怎麼滴。
會發現x只能是\(3^x*2^y\)因此設\(f[i][k][j]\)表示\(x=2^i*3^k y=j\)的狀態
SG定理
有n個遊戲,問先手必勝,仍是先手必敗。
SG定理:
必敗態的sg值=0
SG[i] = mex(i能轉移到的狀態的SG值)
n個遊戲,一塊兒玩的SG值=每一個遊戲SG值亦或起來的結果.
經典問題:NIM石子游戲.
N堆石子,第i堆石子的個數是\(a_i\),每次能夠對任意一堆石子拿走任意多個石子
兩種作法:1.SG定理DP 2.轉化爲NIM遊戲