codeforces-1138 (div2)
想法题是硬伤,面对卡题和卡bug的情况应对能力太差
A.求两个前缀和以及两个后缀和,相邻最小值的最大值。
#include<iostream> using namespace std; const int maxn = 1e5 + 10; int a[maxn]; int pre1[maxn],pre2[maxn],erp1[maxn],erp2[maxn]; int main(){ int N; scanf("%d",&N); for(int i = 1; i <= N ; i ++){ scanf("%d",&a[i]); if(a[i] == 1) pre1[i] = pre1[i - 1] + 1; else pre2[i] = pre2[i - 1] + 1; } for(int i = N; i >= 1; i --){ if(a[i] == 1) erp1[i] = erp1[i + 1] + 1; else erp2[i] = erp2[i + 1] + 1; } int ans = 0; for(int i = 1; i <= N; i ++){ ans = max(ans,min(pre1[i],erp2[i + 1])); ans = max(ans,min(pre2[i],erp1[i + 1])); } printf("%d",ans * 2); return 0; }
B.将人分为00,10,01,11四种,总人数已知为ABCD,假设我们需要选择的分别为abcd。
得到方程1.a + b + c + d = n / 2 方程2 b + d = (C - c) + (D - d)
化简方程2得到b + c + 2d = C + D;
CD为已知常数,考虑暴力枚举dc,可推得b,根据方程1可推得a,然后check,时间复杂度O(n²)
#include<iostream> #include<vector> #include<algorithm> #define pb push_back using namespace std; const int maxn = 5000; struct Node{ int a,b; int id; }node[maxn]; char str[maxn]; int Stack[maxn],top; vector<int>ans; bool cmp(Node a,Node b){ return a.a + a.b > b.a + b.b; } vector<int>aa,ab,ba,bb; int main(){ int N; scanf("%d",&N); scanf("%s",str + 1); for(int i = 1; i <= N ; i ++){ node[i].a = str[i] - '0'; node[i].id = i; } scanf("%s",str + 1); for(int i = 1; i <= N ; i ++) node[i].b = str[i] - '0'; for(int i = 1; i <= N ; i ++){ if(node[i].a + node[i].b == 0) aa.pb(i); else if(node[i].a == 1 && node[i].b == 0) ba.pb(i); else if(node[i].a == 0 && node[i].b == 1) ab.pb(i); else bb.pb(i); } int A = aa.size(),B = ba.size(),C = ab.size(),D = bb.size(); for(int d = 0; 2 * d <= C + D && d <= D; d++){ for(int c = 0; c <= C && c + 2 * d <= C + D;c++){ int b = (C - c) + (D - d) - d; if(b < 0 || b > B) continue; int a = (N / 2) - b - c - d; if(a < 0 || a > A) continue; for(int i = 0 ; i < a; i ++) printf("%d ",aa[i]); for(int i = 0 ; i < b; i ++) printf("%d ",ba[i]); for(int i = 0 ; i < c; i ++) printf("%d ",ab[i]); for(int i = 0 ; i < d; i ++) printf("%d ",bb[i]); return 0; } } puts("-1"); return 0; }
C.对每行每列分别离散化,对于一个点的x是交叉点在横竖往前排名较后的位置 加上往后排名较前的位置。
#include<iostream> #include<vector> #include<algorithm> #define pb push_back #define LL long long using namespace std; const int maxn = 2010; int N,M; LL MAP[maxn][maxn]; LL sr[maxn][maxn],sc[maxn][maxn],hr[maxn][maxn],hc[maxn][maxn]; LL Hash[maxn]; int main(){ scanf("%d%d",&N,&M); for(int i = 1; i <= N; i ++){ for(int j = 1; j <= M ; j ++){ scanf("%lld",&MAP[i][j]); } } for(int i = 1;i <= N ; i ++){ for(int j = 1; j <= M; j ++) Hash[j] = MAP[i][j]; sort(Hash + 1,Hash + 1 + M); int cnt = unique(Hash + 1,Hash + 1 + M) - Hash - 1; for(int j = 1; j <= M ; j ++){ int t = lower_bound(Hash + 1,Hash + 1 + cnt,MAP[i][j]) - Hash; sc[i][j] = t; hc[i][j] = cnt - t; } } for(int j = 1; j <= M ; j ++){ for(int i = 1; i <= N ; i ++) Hash[i] = MAP[i][j]; sort(Hash + 1,Hash + 1 + N); int cnt = unique(Hash + 1,Hash + 1 + N) - Hash - 1; for(int i = 1; i <= N; i ++){ int t = lower_bound(Hash + 1,Hash + 1 + cnt,MAP[i][j]) - Hash; sr[i][j] = t; hr[i][j] = cnt - t; } } for(int i = 1; i <= N; i ++){ for(int j = 1; j <= M; j ++){ if(j != 1) printf(" "); printf("%lld",max(sc[i][j],sr[i][j]) + max(hr[i][j],hc[i][j])); } puts(""); } return 0; }
D.没看清楚题目意思为子串之间可以相互重叠,需要求出一个kmp的next数组,然后贪心的迭代最多能放下的数字。
#include<iostream> #include<vector> #include<algorithm> #include<cstring> #define pb push_back #define LL long long using namespace std; const int maxn = 5e5 + 10; int N,M; char str1[maxn],str2[maxn]; int Z,O,z,o; void get_next(char x[],int m,int nxt[]){ int j = 0; nxt[1] = 0; for(int i = 2; i <= m ; i ++){ while(j && x[i] != x[j + 1]) j = nxt[j]; if(x[j + 1] == x[i]) j ++; nxt[i] = j; } } int Next[maxn]; int main(){ Z = O = z = o = 0; scanf("%s%s",str1 + 1,str2 + 1); for(int i = 1;str1[i]; i ++){ if(str1[i] == '0') Z++; else O++; } for(int i = 1;str2[i]; i ++){ if(str2[i] == '0') z++; else o++; } if(z > Z || o > O){ for(int i = 0 ; i < O; i ++) printf("1"); for(int i = 0 ; i < Z; i ++) printf("0"); }else{ Z -= z; O -= o; int l = strlen(str2 + 1); get_next(str2,l,Next); o = z = 0; int ans = 1; for(int i = Next[l] + 1; i <= l ; i ++){ if(str2[i] == '1') o++; else z++; } while(Z - z >= 0 && O - o >= 0){ ans++; Z -= z; O -= o; } printf("%s",str2 + 1); for(int i = 1 ; i < ans; i ++){ for(int j = Next[l] + 1; j <= l; j ++) printf("%c",str2[j]); } for(int i = 0 ; i < O; i ++) printf("1"); for(int i = 0 ; i < Z; i ++) printf("0"); } return 0; }
E.很显然,如果不考虑去重,是一个记录dp[100000][50]的最短路模型,当然,对于这题是不可以的。
对于这样的模型,可以考虑分层图,将这些点拆分为1e5 * 50个点,用所给的条件重新建图。
好处就是可以Tarjan缩点之后跑dp,问题事实上在于前一个连通分量的点和后一个连通分量的点会不会出现同一天的问题。
很显然是不会的,如果一个点可以在星期2到达,然后绕几圈在星期4到达,很显然他绕几圈之后还会在星期2回来,也就是说,2和4事实上可以保证在同一个连通分量里面。
#include<iostream> #include<queue> #include<cstdio> #include<cstdlib> #define mp make_pair #define PII pair<int,int> #define pb push_back #define fi first #define se second using namespace std; const int maxn = 1e5 * 55 + 10; struct Edge{ int to,next; }edge[maxn * 2]; int head[maxn],tot; char str[maxn]; int N,M,D; void init(){ for(int i = 0 ; i <= N * D; i ++) head[i] = -1; tot = 0; } void add(int u,int v){ edge[tot].to = v; edge[tot].next = head[u]; head[u] = tot++; } int low[maxn],dfn[maxn],Stack[maxn],Index,top,scc,num[maxn],belong[maxn]; bool InStack[maxn]; void Tarjan(int u){ int v; low[u] = dfn[u] = ++Index; Stack[top++] = u; InStack[u] = true; for(int i = head[u]; ~i ; i = edge[i].next){ int v = edge[i].to; if(!dfn[v]){ Tarjan(v); if(low[u] > low[v]) low[u] = low[v]; }else if(InStack[v] && low[u] > dfn[v]) low[u] = dfn[v]; } if(low[u] == dfn[u]){ scc++; do{ v = Stack[--top]; InStack[v] = false; belong[v] = scc; }while(v != u); } } bool MAP[100010][55]; vector<int>P[maxn]; int vis[maxn]; int ind[maxn]; int dp[maxn]; inline int cul(int x,int y){ return (x - 1) * D + y + 1; } int main(){ scanf("%d%d%d",&N,&M,&D); init(); for(int i = 1; i <= M ; i ++){ int u,v; scanf("%d%d",&u,&v); for(int d = 0;d < D; d ++){ int t = (d + 1) % D; add(cul(u,d),cul(v,t)); // cout << cul(u,d) << " " << cul(v,t) << endl; } } for(int i = 1; i <= N ; i ++){ scanf("%s",str); for(int j = 0 ; j < D; j ++){ MAP[i][j] = str[j] - '0'; } } for(int i = 1; i <= N * D; i ++) if(!dfn[i]) Tarjan(i); top = 0; for(int i = 1; i <= N ; i ++){ for(int d = 0 ; d < D; d ++){ if(!MAP[i][d]) continue; int id = belong[cul(i,d)]; if(vis[id]) continue; num[id]++; vis[id] = 1; Stack[++top] = id; } for(int j = 1; j <= top; j ++){ vis[Stack[j]] = 0; } top = 0; } for(int i = 1; i <= N * D; i ++){ for(int j = head[i]; ~j ; j = edge[j].next){ int v = edge[j].to; if(belong[i] != belong[v]){ ind[belong[i]]++; P[belong[v]].pb(belong[i]); } } } queue<int>Q; for(int i = 1; i <= scc; i ++){ if(!ind[i]){ dp[i] = num[i]; Q.push(i); } } while(!Q.empty()){ int u = Q.front(); Q.pop(); for(int i = 0 ; i < P[u].size(); i ++){ int v = P[u][i]; dp[v] = max(dp[v],dp[u] + num[v]); ind[v]--; if(!ind[v]) Q.push(v); } } int ans = dp[belong[1]]; printf("%d\n",ans); return 0; }