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> usingnamespace 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"); } }