今天考试又是再一次考得很屎...第一题只狗了十分...第二题十分..第三题直接挂掉了...。
但是我第三题是想出正解了的!!!但是我就是写挂了.. 少写了一句话就全挖挖了
这道题姜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$的贡献即可 和快速幂的思想是一样的
代码
#includeusing 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 23 1 1 0 1 2 10 2 1 20 2 35 1 1 0 1 2 10 1 3 20 2 1 30 2 3 40 2 23 1 1 20 1 2 10 2 1 0 3 34 1 1 0 1 3 10 3 1 10 3 3 20 2 24 1 1 0 1 2 10 2 1 30 2 2 20 1 1 1 1 1 -1
输出
YesNoNoYesNoNo
这道题谁来告诉我他是怎么通过网格图想到带权并查集的!!!!
首先考虑每个$2 * 2$的正方形满足$l1 + r2 = l2 + r1 -> l1 - l2 = r1 - r2$ 也就是说相邻两行的差是处处相等的 可以推出任意两行的差都相等 列也是一样的
所以对于列相等的两个点 他们所在的行可以使用带权并查集进行合并 某个行的路径权值记作他到他父亲那一行的$val$之差
所以通过给定的条件进行合并与判断是否合法 若两行之前未合并 将其合并即可 否则则判断他们目前的差是否等于他们的路径权值之差
而对于路径权值的合并 假设是由$a$合向$b$ 就有$w[fa_a] = val + w[b] - w[a]$ 画图会比较好推出来
这个时候解决了行之间合并的问题 现在问题还有检查是否非负 所以对于每一个点 都求出它所对应的父亲行里的最小点权 在对于每一行求出一个它对应的差值最大的行
所以每一行的最小点权减去最大差值取$min$就可以得到最小点权 判断他是否非负即可。
代码
#include#define oo 1e9using 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#define il inline#define rg registerusing 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"); }}