1. 程式人生 > >18.11.1 考試總結

18.11.1 考試總結

今天考試又是再一次考得很屎...第一題只狗了十分...第二題十分..第三題直接掛掉了...。

但是我第三題是想出正解了的!!!但是我就是寫掛了.. 少寫了一句話就全挖挖了

   

 

 


 

這道題姜sir用題目中原根的做法做的,,,150行 然而yyyuuu大佬有個非常牛逼的做法只有50行

首先根據期望的定義 答案的計算方法一定是 貢獻 / 概率 概率顯然是$n^{m}$

所以主要問題就是計算貢獻了 也就是所有的$a[i] * x % mod$進行若干次操作之後的答案和 考慮$dp$求這個東西

$dp[i][j]$表示到了第$i$次操作模$mod$之後餘$j$的情況數 那麼最終答案就是$∑dp[m][j] * j$

轉移是$dp[i][j] -> dp[i  + 1][j * a[k] Mod mod]$但是由於第一維過大 是$1e9$的範圍 所以考慮二進位制優化這維

這個時候只需要$dp[j]$表示到目前對應$m$的第$i$位 也就是進行了$1 << i$次操作之後對應餘數為$j$的情況數

那麼考慮兩個區間是如何合併的 對於兩個區間中的$dp$值 $dp[i]$與$dp[j]$可以使用乘法原理更新$dp[i * j Mod mod]$

若$m$當前這一位是$1$就統計$dp$的貢獻即可 和快速冪的思想是一樣的

程式碼

#include <bits/stdc++.h> 
using
namespace std; typedef long long ll; const int N = 1e5 + 5; const ll MOD = 1e9 + 7; ll g[N], f[N], h[N], n, m, mod; ll fast_pow(ll a, ll b) { ll ans = 1; for(; b; b >>= 1, a = a * a % MOD) if(b & 1) ans = ans * a % MOD; return ans; } int main( ) { freopen(
"rand.in", "r", stdin); freopen("rand.out", "w", stdout); scanf("%lld%lld%lld",& n,& m,& mod); for(int i = 1;i <= n;i ++) {int x; scanf("%d", & x); f[x] ++;} g[1] = 1; ll M = m; while(m) { if(m & 1) { for(int i = 0;i < mod;i ++) h[i] = 0; for(int i = 0;i < mod;i ++) for(int j = 0;j < mod;j ++) h[i * j % mod] = (h[i * j % mod] + g[i] * f[j] % MOD) % MOD; for(int i = 0;i < mod;i ++) g[i] = h[i]; } for(int i = 0;i < mod;i ++) h[i] = 0; for(int i = 0;i < mod;i ++) for(int j = 0;j < mod;j ++) h[i * j % mod] = (h[i * j % mod] + f[i] * f[j] % MOD) % MOD; for(int i = 0;i < mod;i ++) f[i] = h[i]; m >>= 1; } ll ans = 0; for(int i = 1;i < mod;i ++) ans = (ans + i * g[i] % MOD) % MOD; ans = ans * fast_pow(fast_pow(n, M), MOD - 2) % MOD; printf("%lld\n", ans); }

 

       

           

這道題樣例又臭又長

輸入

6 
2 2
3 
1 1 0 
1 2 10 
2 1 20 
2 3
5 
1 1 0 
1 2 10 
1 3 20 
2 1 30 
2 3 40 
2 2
3 
1 1 20 
1 2 10 
2 1 0 
3 3
4 
1 1 0 
1 3 10 
3 1 10 
3 3 20 
2 2
4 
1 1 0 
1 2 10 
2 1 30 
2 2 20 
1 1
 1 
1 1 -1 

輸出

Yes
No
No
Yes
No
No

這道題誰來告訴我他是怎麼通過網格圖想到帶權並查集的!!!!

首先考慮每個$2 * 2$的正方形滿足$l1 + r2 = l2 + r1 -> l1 - l2 = r1 - r2$ 也就是說相鄰兩行的差是處處相等的 可以推出任意兩行的差都相等 列也是一樣的

所以對於列相等的兩個點 他們所在的行可以使用帶權並查集進行合併 某個行的路徑權值記作他到他父親那一行的$val$之差

所以通過給定的條件進行合併與判斷是否合法 若兩行之前未合併 將其合併即可 否則則判斷他們目前的差是否等於他們的路徑權值之差

而對於路徑權值的合併 假設是由$a$合向$b$ 就有$w[fa_a] = val + w[b] - w[a]$ 畫圖會比較好推出來

這個時候解決了行之間合併的問題 現在問題還有檢查是否非負 所以對於每一個點 都求出它所對應的父親行裡的最小點權 在對於每一行求出一個它對應的差值最大的行

所以每一行的最小點權減去最大差值取$min$就可以得到最小點權 判斷他是否非負即可。

程式碼

#include <bits/stdc++.h>
#define oo 1e9
using namespace std;

const int N = 1e5 + 5;
int fax[N], fay[N], wx[N], wy[N], T, R, C;
int n, mi1[N], mi2[N];
struct node {
    int x, y, val;
    void read( ) {
        scanf("%d%d%d",& x,& y,& val);
    }
}P[N];

bool cmpx(const node & a, const node & b) {
    return a.x < b.x;
}

bool cmpy(const node & a, const node & b) {
    return a.y < b.y;
}

int find_x(int u) {
    
    if(fax[u] == u) return u;
    int rt = find_x(fax[u]);
    wx[u] += wx[fax[u]];
    return fax[u] = rt;
}

bool link_x(int u, int v, int w) {
    
    int fu = find_x(u), fv = find_x(v);
    if(fu != fv) {
        fax[fu] = fv;
        wx[fu] = w + wx[v] - wx[u]; return true;
    }
    else return wx[u] == wx[v] + w;
}

int find_y(int u) {
    
    if(fay[u] == u) return u;
    int rt = find_y(fay[u]);
    wy[u] += wy[fay[u]];
    return fay[u] = rt;
}

bool link_y(int u, int v, int w) {
    
    int fu = find_y(u), fv = find_y(v);
    if(fu != fv) {
        fay[fu] = fv;
        wy[fu] = w + wy[v] - wy[u]; return true;
    }
    else return wy[u] == wy[v] + w;
}

int main( ) {
    
    freopen("then.in", "r", stdin);
    freopen("then.out", "w", stdout);
    scanf("%d", & T);
    while(T --) {
        bool tag = true;
        scanf("%d%d%d",& R,& C,& n);
        for(int i = 1;i <= R;i ++) {
            fax[i] = i; wx[i] = 0; 
        }
        for(int i = 1;i <= C;i ++) {
            fay[i] = i; wy[i] = 0;
        }
        for(int i = 1;i <= n;i ++) {
            P[i].read( );
            if(P[i].val < 0) tag = false;
        }
        sort(P + 1, P + n + 1, cmpx);
        for(int i = 1;i < n;i ++) {
            if(P[i].x == P[i + 1].x) 
                if(! link_y(P[i].y, P[i + 1].y, P[i + 1].val - P[i].val)) tag = false;
        }
        sort(P + 1, P + n + 1, cmpy);
        for(int i = 1;i < n;i ++) {
            if(P[i].y == P[i + 1].y)
                if(! link_x(P[i].x, P[i + 1].x, P[i + 1].val - P[i].val)) tag = false;
        }
        memset(mi1, 0x3f3f, sizeof(mi1));
        memset(mi2, 0x3f3f, sizeof(mi2));
        for(int i = 1;i <= n;i ++) {
            int rt = find_x(P[i].x);
            mi1[rt] = min(mi1[rt], P[i].val + wx[P[i].x]);
        }
        for(int i = 1;i <= R;i ++) {
            int rt = find_x(i);
            mi2[rt] = min(mi2[rt], -wx[i]);
        }
        int cmp = oo;
        for(int i = 1;i <= R;i ++) cmp = min(cmp, mi1[i] + mi2[i]);
        if(cmp < 0) tag = false;
        if(tag) printf("Yes\n");
        else printf("No\n");
    }
}

 

 

       

“今年的槍斃名單已經確定了”

這道題是最簡單的!! 就是一個貪心啊 首先可以發現魔法師走路是毫無用處的 距離依舊在減小 並且傷害也在減小

所以就求出廚師距離 能夠攻擊就攻擊 不夠就回能量 每次記者選擇最優路徑即可.. 我少寫了消耗能量那一句話...。

程式碼

#include <bits/stdc++.h>
#define il inline
#define rg register
using namespace std;

typedef long long ll;
int xx, yy, x2, y2, d1, d2, c, d, T, a, b;

il int read( ) {
    
    int t = 1, ans = 0;
    char x; x = getchar( );
    while(x < '0' || x > '9') {
        if(x == '-') t = -1;
        x = getchar( );
    }
    while(x >= '0' && x <= '9') {
        ans = ans * 10 + x - '0';
        x = getchar( );
    }
    return ans * t;
}

il bool solve( ) {
    
    xx = read( ), yy = read( ), x2 = read( ), y2 = read( );
    c = read( ), d = read( );
    if(xx == x2 && yy == y2) {
        if(d <= 0) return true; 
        return false;
    }
    int d1 = abs(xx - x2);
    int d2 = abs(yy - y2);
    while(1) {
        if(d <= 0) return true;
        if(!d1 && !d2) return false;
        int del = d1 * d1 + d2 * d2;
        if(c < a) c += b;
        else d -= del, c -= a;
        if(d <= 0) return true;
        int x, y;
        for(int i = 1; i <= 2; i ++) {
            if(d1 == 0 && d2 == 0)    return false;
            if(d1 <= d2)    d2 --;
            else d1 --;    
        }
    }
}

int main( ) {
    
    freopen("do.in", "r", stdin);
    freopen("do.out", "w", stdout);
    T = read( ), a = read( ), b = read( );
    while(T --) {
        bool ans = solve( );
        if(ans) printf("NAIVE\n");
        else printf("SIMPLE\n");
    }
}