整理

Life and death

Day1

模擬賽部分

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調試排序

Day2

模擬賽部分

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]隨機數生成器

矩陣優化遞推。

一些奇奇怪怪的NPC問題:

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遊戲

相關文章
相關標籤/搜索