去年十月拿了省一真是挺开心的。。。。
总算是订正得差不多了。。。(斗地主一题实在受不了!!!)
D1T1 神奇的幻方
裸模拟就不说了。。
#include<iostream> #include<cstdio> using namespace std; const int maxn = 50; int a[maxn][maxn],i,j,n,r,c; int main() { cin >> n; int mr = n / 2 + 1; int mc = mr; a[1][mc] = 1; r = 1; c = mc; for (i = 2; i <= n*n; i++) { if (r == 1 && c != n) { a[n][c+1] = i; r = n; c = c+1; continue; } if (r != 1 && c == n) { a[r-1][1] = i; r = r-1; c = 1; continue; } if (r == 1 && c == n) { a[r+1][c] = i; r = r+1; continue; } if (!a[r-1][c+1]) { a[r-1][c+1] = i; r = r-1; c = c+1; } else { a[r+1][c] = i; r = r+1; } } for (i = 1; i <= n; i++) { for (j = 1; j < n; j++) printf("%d ",a[i][j]); printf("%d\n",a[i][j]); } return 0; }
D1T2 信息传递
显然找到图中最大环即可
苟蒻用tarjan算法。。不过还好栈有8MB。。比赛的时候完全没考虑过爆栈
#include<iostream> #include<cstdio> #include<cstring> #include<stack> using namespace std; const int maxn = 2e5 + 10; int sccno[maxn],pre[maxn],low[maxn],Size[maxn],To[maxn],n,i,j,ans = 1E9,d_t = 0,cur = 0; stack <int> s; void dfs(int k) { pre[k] = low[k] = ++d_t; s.push(k); if (!pre[To[k]]) { dfs(To[k]); low[k] = min(low[k],low[To[k]]); } else { if (!sccno[To[k]]) low[k] = min(low[k],low[To[k]]); } if (pre[k] == low[k]) { int S = 0; ++cur; while (1) { int now = s.top(); sccno[now] = cur; ++S; s.pop(); if (now == k) break; } Size[cur] = S; } } int main() { cin >> n; for (i = 1; i <= n; i++) scanf("%d",&To[i]); memset(pre,0,sizeof(pre)); for (i = 1; i <= n; i++) if (!pre[i]) dfs(i); for (i = 1; i <= n; i++) if (Size[sccno[i]] != 1) ans = min(ans,Size[sccno[i]]); cout << ans; return 0; }
D1T3 斗地主
苟蒻终于在2016.3.12A了这道狗日的深搜
提炼一道正解的观点
除了三种龙以外,其他牌同牌点顺序无关,见qwork函数直接算出
剩下就是枚举龙了
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 15; int n,t,ans,b[4],a[maxn]; int qwork() { int c1,c2,c3,c4,cnt; c1 = c2 = c3 = c4 = cnt = 0; for (int i = 0; i < 14; i++) { if (a[i] == 1) ++c1; if (a[i] == 2) ++c2; if (a[i] == 3) ++c3; if (a[i] == 4) ++c4; } while (c4 && c2 > 1) --c4,c2-=2,++cnt; while (c4 && c1 > 1) --c4,c1-=2,++cnt; while (c3 && c2) --c3,--c2,++cnt; while (c3 && c1) --c3,--c1,++cnt; while (c4 && c2) --c4,--c2,++cnt; return cnt+c1+c2+c3+c4; } void dfs(int now,int rest) { if (now >= ans) return; int ret = qwork(); ans = min(ans,now+ret); for (int i = 0; i <= 7; i++) if (a[i]) { int j = i; while (a[j]) ++j; if (j - i >= 5) { --j; j = min(j,11); for (int l = j; l >= i+4; l--) { for (int k = i; k <= l; k++) --a[k]; dfs(now+1,rest-l+i-1); for (int k = i; k <= l; k++) ++a[k]; } } } for (int i = 0; i <= 9; i++) if (a[i] >= 2) { int j = i; while (a[j] >= 2) ++j; if (j - i >= 3) { --j; j = min(j,11); for (int l = j; l >= i+2; l--) { for (int k = i; k <= l; k++) a[k] -= 2; dfs(now+1,rest-2*(l-i+1)); for (int k = i; k <= l; k++) a[k] += 2; } } } for (int i = 0; i <= 10; i++) if (a[i] >= 3) { int j = i; while (a[j] >= 3) ++j; if (j - i >= 2) { --j; j = min(j,11); for (int l = j; l >= i+1; l--) { for (int k = i; k <= l; k++) a[k] -= 3; dfs(now+1,rest-3*(l-i+1)); for (int k = i; k <= l; k++) a[k] += 3; } } } } void READ() { memset(a,0,sizeof(a)); for (int i = 1; i <= n; i++) { int x,y; scanf("%d%d",&x,&y); if (x > 2) x -= 3; else if (!x) x = 13; else x += 10; ++a[x]; } } int main() { #ifdef YZY freopen("yzy.txt","r",stdin); #endif cin >> t >> n; while (t--) { READ(); ans = n; dfs(0,n); printf("%d\n",ans); } return 0; }
D2T1 跳石头
简单的二分,比赛的时候竟然忘了特判最后一枚石头(即终点)
还好那只有一个点。。。
#include<iostream> #include<cstdio> using namespace std; const int maxn = 5e4 + 50; int s[maxn],l,m,n,i,j; bool Judge(int now) { int rig = 0; int sum = 0; for (i = 1; i <= n; i++) { if (s[i] - rig < now) ++sum; else rig = s[i]; } if (l - rig < now) ++sum; return sum <= m; } int main() { cin >> l >> n >> m; for (i = 1; i <= n; i++) scanf("%d",&s[i]); int L = 0,R = l+10; while (R - L > 1) { int mid = (R+L) >> 1; if (Judge(mid)) L = mid; else R = mid; } if (Judge(R)) cout << R; else cout << L; return 0; }
D2T2 子串
dp。。。
然而苟蒻太水现场不会做只骗了40分
f[k][i][j]:第一个串到i位置第二个串到j位置,且用第一个串的i字符成为第二个串的j字符,已选用k段的方案数
那么方案转移就变为延续上一种方案的尾部或开一段新的子串
详见代码。。
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #include<cmath> #include<vector> #include<queue> using namespace std; typedef long long LL; const LL mo = 1000000007; LL f[2][1010][210],sum[2][1010][210]; char s1[1010],s2[210]; int n,m,k,i,j; int main() { cin >> n >> m >> k; scanf("%s",1+s1); scanf("%s",1+s2); f[0][0][0] = 1; for (i = 0; i <= n; i++) sum[0][i][0] = 1; for (int l = 1; l <= k; l++) { memset(f[l&1],0,sizeof(f[l&1])); memset(sum[l&1],0,sizeof(sum[l&1])); for (i = 1; i <= n; i++) for (j = 1; j <= m; j++) { if (s1[i] == s2[j]) { f[l&1][i][j] = sum[(l-1)&1][i-1][j-1]; if (s1[i-1] == s2[j-1]) f[l&1][i][j] = (f[l&1][i][j]+f[l&1][i-1][j-1]) % mo; } sum[l&1][i][j] = (f[l&1][i][j] + sum[l&1][i-1][j]) % mo; } } cout << sum[k&1][n][m]; return 0; }
D2T3
比赛强行朴素算法35分。。
据说只有O(n)的神算法才能AC?
苟蒻学了O(nlogn)的使用树上前缀和的算法
显然每个运输方案对应树上1或2条链
用倍增算法求出每个方案初始耗时,经过的路径
二分答案,用树上前缀和求之
(bzoj上竟然可以过)
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<queue> using namespace std; const int maxn = 3E5 + 10; typedef int LL; struct E{ LL to,w,num; }; struct Q{ LL x,y,sum,fa; }q[maxn]; LL n,m,i,j,du[maxn],pass[maxn]; LL anc[maxn][20],sum[maxn][20],t[maxn],vis[maxn],L[maxn],Fa[maxn],Mark[maxn]; vector <E> v[maxn]; vector <LL> Last; queue <LL> qu; queue <LL> qu2; void Bfs(int x) { qu2.push(x); while (!qu2.empty()) { bool flag = 0; LL k = qu2.front(); qu2.pop(); for (int l = 0; l < v[k].size(); l++) { int to = v[k][l].to; if (L[to]) continue; flag = 1; L[to] = L[k] + 1; anc[to][0] = k; sum[to][0] = v[k][l].w; Fa[to] = v[k][l].num; ++du[k]; qu2.push(to); } if (!flag) Last.push_back(k); } } void LCA_pre() { for (int l = 1; l < 20; l++) for (i = 1; i <= n; i++) { anc[i][l] = anc[anc[i][l-1]][l-1]; sum[i][l] = sum[anc[i][l-1]][l-1] + sum[i][l-1]; } } void solve() { LL x = q[i].x; LL y = q[i].y; LL &S = q[i].sum; LL &fa = q[i].fa; if (L[x] < L[y]) swap(x,y); LL log; for (log = 0; L[x] - (1<<log) > 0; ++log); for (j = log; j >= 0; j--) if (L[x] - (1<<j) >= L[y]) { S += sum[x][j]; x = anc[x][j]; } if (x == y) { fa = y; return; } for (j = log; j >= 0; j--) if (anc[x][j] != anc[y][j]) { S += sum[x][j] + sum[y][j]; x = anc[x][j]; y = anc[y][j]; } S += sum[x][0] + sum[y][0]; fa = anc[x][0]; } bool Judge(int now) { memset(vis,0,sizeof(vis)); memset(Mark,0,sizeof(Mark)); LL tot = 0,Max = 0; for (i = 1; i <= m; i++) if (q[i].sum > now) { ++Mark[q[i].x]; ++Mark[q[i].y]; Mark[q[i].fa] -= 2; ++tot; Max = max(Max,q[i].sum); } memset(pass,0,sizeof(pass)); for (int l = 0; l < Last.size(); l++) qu.push(Last[l]); while (!qu.empty()) { LL k = qu.front(); qu.pop(); vis[Fa[k]] += Mark[k]; Mark[anc[k][0]] += Mark[k]; k = anc[k][0]; ++pass[k]; if (pass[k] == du[k]) qu.push(k); } LL tmp = 0; for (i = 1; i < n; i++) if (vis[i] == tot) tmp = max(tmp,t[i]); return Max - tmp <= now; } int main() { cin >> n >> m; memset(anc,-1,sizeof(anc)); for (i = 1; i < n; i++) { LL x,y,w; scanf("%d%d%d",&x,&y,&w); v[x].push_back((E){y,w,i}); v[y].push_back((E){x,w,i}); t[i] = w; } L[1] = 1; Bfs(1); LCA_pre(); for (i = 1; i <= m; i++) { scanf("%d%d",&q[i].x,&q[i].y); solve(); } int l = -1,r = 3E8 + 30; while (r - l > 1) { LL mid = (l+r) >> 1; if (Judge(mid)) r = mid; else l = mid; } if (Judge(l)) cout << l; else cout << r; return 0; }