---恢复内容开始---
题解;
第一题:简单状压 dp[i][s]表示处理到第i位,他的上一位(1),当前位置(2),下一位的状态为s的方案数(4);
我每次保证i这个s状态合法,然后往下讨论i+1的情况;
#include<bits/stdc++.h> using namespace std; const int mod = 1e9 + 7; const int M = 1e6 + 5; int dp[M][(1<<3)+1]; inline int go(char a){ switch(a){ case '?': return 3; case '2': return 2; case '1': return 1; case '0': return 0; case '*': return 4; } } inline int up(int &x, int b){ x += b; if(x >= mod) x -= mod; } char ss[M]; int main(){ freopen("mine.in","r",stdin); freopen("mine.out","w",stdout); scanf("%s", ss); int len = strlen(ss); int t = go(ss[0]); if(t == 0) dp[0][0] = 1; else if(t == 1) dp[0][4] = 1; else if(t == 4) dp[0][2] = 1, dp[0][6] = 1; else if(t == 3) dp[0][0] = dp[0][2] = dp[0][4] = dp[0][6] = 1; for(int i = 0; i < len - 1; i++) for(int s = 0; s < (1<<3); s++){ if(dp[i][s]){ int t = go(ss[i+1]); int p1 = s&2 ? 1 : 0, p2 = s&4 ? 1 : 0; if(t == 0){ if(!p1 && !p2) up(dp[i+1][s>>1], dp[i][s]); } else if(t == 1){ if(p1 && !p2) up(dp[i+1][s>>1], dp[i][s]); if(!p1 && !p2) up(dp[i+1][s>>1|4], dp[i][s]); } else if(t == 2){ if(p1 && !p2) up(dp[i+1][s>>1|4], dp[i][s]); } else if(t == 3){ up(dp[i+1][s>>1], dp[i][s]); up(dp[i+1][s>>1|4], dp[i][s]); } else if(t == 4){ if(p2) { up(dp[i+1][s>>1], dp[i][s]); up(dp[i+1][s>>1|4], dp[i][s]); } } //printf("%d %d %d\n", i, s, dp[i][s]); } } int ans = 0; t = go(ss[len-1]); if(t == 0) up(ans, dp[len-1][0]); else if(t == 1) up(ans, dp[len-1][1]); else if(t == 4) up(ans, dp[len-1][3]), up(ans, dp[len-1][2]); else if(t == 3) up(ans, dp[len-1][0]), up(ans, dp[len-1][1]), up(ans, dp[len-1][2]), up(ans, dp[len-1][3]); printf("%d\n", ans); }
第二题:
这道题可以爆搜+剪枝过;对于一个坑,
1.他流出去变成0;
2.周围太高,他被累积在里面;
1可以暴力bfs+记忆化 O(N*N);
2.比他低的我就流出去覆盖,比他高的就在七中找最小值,还是加一个记忆化;
以下就是zyy大佬的优美搜索:
#include<bits/stdc++.h> inline int read() { char c; int res = 0, f = 1; while((c = getchar()) < '0' || c > '9') if(c == '-') f = -f; while(c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); } return res * f; } const int MAXN = 300 + 5; const int INF = 0x7fffffff; int dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0}; int n, m; int h[MAXN][MAXN], ans[MAXN][MAXN], vis1[MAXN][MAXN], vis2[MAXN][MAXN]; int dft, tmp; bool inMap(int x, int y) { return x >= 0 && y >= 0 && x <= n + 1 && y <= m + 1; } void dfr(int x, int y) { if(vis1[x][y]) return; vis1[x][y] = true; for(int k = 0; k < 4; k++) { int xx = x + dx[k], yy = y + dy[k]; if(!inMap(xx, yy)) { ans[x][y] = 0; } else if(h[xx][yy] <= h[x][y]){ dfr(xx, yy); if(ans[xx][yy] == 0) ans[x][y] = 0; } } } bool dfs(int x, int y, int u) { if(vis2[x][y] == dft) return true; if(h[x][y] > u) { tmp = std::min(tmp, h[x][y] + ans[x][y]); return true; } if(ans[x][y] == 0) return false; vis2[x][y] = dft; for(int k = 0; k < 4; k++) { int xx = x + dx[k], yy = y + dy[k]; if(!dfs(xx, yy, u)) return false; } return true; } struct Point { int x, y, height; bool operator < (const Point &t) const { return height > t.height; } } points[MAXN * MAXN]; int main() { freopen("water.in", "r", stdin); freopen("water.out", "w", stdout); n = read(), m = read(); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) { h[i][j] = read(); points[(i - 1) * m + j] = (Point) {i, j, h[i][j]}; } std::sort(points + 1, points + 1 + n * m); memset(ans, -1, sizeof(ans)); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) dfr(i, j); for(int i = 1; i <= n * m; i++) { dft++; tmp = INF; if(!dfs(points[i].x, points[i].y, points[i].height)) ans[points[i].x][points[i].y] = 0; else ans[points[i].x][points[i].y] = tmp - points[i].height; } for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) printf("%d ", ans[i][j]); printf("\n"); } }
正解:最小生成树;
对于可以走出去(矩形外)的,这个路径上的最大值是可以确定的,我们考虑在里面的点和外界连通,那么他第一次通向外界的边就是当前最小生成树加上的这条边,而且这条边是通向外界的最小路径上的最大边(思考),所以在里面封闭的一团的答案就是这条边的权值;
#include<bits/stdc++.h> using namespace std; int zl[4][2] = {{0,1}, {1,0}, {-1,0}, {0,-1}}; int ans[305][305], mp[305][305], f[305*305], n, m, pd[305*305], h[320*320], cnt; struct Edge{int u, v, w;}g[900000]; struct edge{int v, nxt;}G[900000]; bool cmp(Edge A, Edge B){return A.w < B.w;} void add(int u, int v){ G[++cnt].v = v, G[cnt].nxt = h[u], h[u] = cnt; } inline int id(int x, int y){ return x*(m+2) + y; } int find(int x){return f[x] == x ? x : f[x] = find(f[x]);} void dfs(int x, int w){ ans[x/(m+2)][x%(m+2)] = w; for(int i = h[x]; i; i = G[i].nxt){ int v = G[i].v; dfs(v, w); } } int main(){ freopen("water.in","r",stdin); freopen("water.out","w",stdout); int tot = 0; scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) scanf("%d", &mp[i][j]); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) for(int k = 0; k < 4; k++){ int x = i + zl[k][0], y = j + zl[k][1]; g[++tot] = (Edge) {id(i, j), id(x, y), max(mp[i][j], mp[x][y])}; } /*for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++)printf("%d ",id(i,j));puts(""); }*/ for(int i = 0; i <= id(n+1, m+1); i++) f[i] = i; for(int i = 0; i <= n+1; i++){ pd[id(i, 0)] = pd[id(i, m+1)] = 1; } for(int i = 0; i <= m+1; i++){ pd[id(0, i)] = pd[id(n+1, i)] = 1; } sort(g + 1, g + 1 + tot, cmp); for(int i = 1; i <= tot; i++){ int fu = find(g[i].u), fv = find(g[i].v); if(fu == fv) continue; if(pd[fu] && !pd[fv]){ dfs(fv, g[i].w); } else if(pd[fv] && !pd[fu]){ dfs(fu, g[i].w); } f[fv] = fu; pd[fu] |= pd[fv]; add(fu, fv); //printf("%d %d %d %d %d %d\n", fu/(m+2), fu%(m+2), fv/(m+2), fv%(m+2), pd[fu], pd[fv]); } for(int i = 1; i <= n; i++){ for(int j = 1; j <= m; j++)printf("%d ", ans[i][j] - mp[i][j]); puts(""); } }
第三题:(为什么我觉得这道题考过,而且当时就是yl大佬讲的?我可以失去了记忆)
求不互质的显然比求互质的更困难,所以我们求互质的;
互质联想到什么,拆分质因数!
容斥:我们减去gcd都是2倍数的贡献,减去是3倍数的贡献,加上是6倍数的贡献……
发现有以下性质:
1.容斥原理对每个数的质因数拆分,奇减偶加;
2.如果一个质因数出现了两次,如2*3*5*5,筛2,3,5的时候就会把后面的贡献处理掉,这个多余的5没用,他整个数对后面也没有用;
所以质因数出现两次,贡献为0;
3.1的贡献是1;
这个恰好符合莫比乌斯函数的性质:https://www.cnblogs.com/peng-ym/p/8647856.html
#include<bits/stdc++.h> using namespace std; #define ll long long const int M = 500005; bool in[M], vis[M]; int a[M], mul[M], tot, prime[M], cnt[M]; void init(){ mul[1] = 1; for(int i = 2; i < M; i++){ if(!vis[i]) prime[++tot] = i, mul[i] = -1; for(int j = 1; j <= tot && i * prime[j] < M; j++){ int m = i * prime[j]; vis[m] = 1; if(i % prime[j] == 0)break; mul[m] = -mul[i]; } } } ll ans = 0; void add(int x, int opt){ for(int i = 1; i * i <= x; i++){ if(x % i)continue; ans -= ( (ll) cnt[i] * (cnt[i] - 1) / 2 * mul[i] ) ; cnt[i] += opt; ans += ( (ll) cnt[i] * (cnt[i] - 1) / 2 * mul[i] ); if(x / i == i)continue; ans -= ( (ll) cnt[x/i] * (cnt[x/i] - 1) / 2 * mul[x/i] ); cnt[x/i] += opt; ans += ( (ll) cnt[x/i] * (cnt[x/i] - 1) / 2 * mul[x/i] ); } } int read(){ int x = 0; int f = 1; char c = getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();} return x*=f; } int main(){ freopen("gcd.in","r",stdin); freopen("gcd.out","w",stdout); init(); int n = read(), m = read(); for(int i = 1; i <= n; i++) a[i] = read(); while(m--){ int u = read(); if(in[u])add(a[u], -1); else add(a[u], 1); in[u] ^= 1; printf("%lld\n", ans); } }
---恢复内容结束---