Contest Page A sol 每次选最小的,然后把它的所有倍数都删掉。 #includebits/stdc++.husing namespace std;int read(){ int a = 0; char c = getchar(); bool f = 0; while(!isdigit(c)){f = c == '-'; c = getchar();} while(isdigit(c
Contest Page
A
sol
每次选最小的,然后把它的所有倍数都删掉。#include<bits/stdc++.h> using namespace std; int read(){ int a = 0; char c = getchar(); bool f = 0; while(!isdigit(c)){f = c == '-'; c = getchar();} while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return f ? -a : a; } set < int > num; int main(){ int N , cnt = 0; cin >> N; for(int i = 1 ; i <= N ; ++i){int a; cin >> a; num.insert(a);} while(!num.empty()){ ++cnt; int val = *num.begin(); for(int i = val ; i <= 100 ; i += val) if(num.find(i) != num.end()) num.erase(i); } cout << cnt; return 0; }
B
sol
因为$lcm(2,3,4,5)=120$,所以在所有灯都开始闪了之后$120sec$之后到达的状态一定一致。所以暴力做$>125$轮即可。#include<bits/stdc++.h> using namespace std; int num[1003] , N , A[1003] , B[1003]; int main(){ cin >> N; for(int i = 1 ; i <= N ; ++i){ char c = getchar(); while(!isdigit(c)) c = getchar(); num[i] = c - '0'; } for(int i = 1 ; i <= N ; ++i) cin >> A[i] >> B[i]; int ans = 0; for(int i = 1 ; i <= N ; ++i) ans += num[i]; for(int i = 1 ; i <= 250 ; ++i){ for(int j = 1 ; j <= N ; ++j) if(i >= B[j] && (i - B[j]) % A[j] == 0) num[j] ^= 1; int sum = 0; for(int j = 1 ; j <= N ; ++j) sum += num[j]; ans = max(ans , sum); } cout << ans; return 0; }
C
sol
枚举$i \in [0 , 8]$,先将$\leq i$的所有数都染$1$,然后将最后染$1$的位置之后的所有数值为$i+1$的位置染$1$,最后把剩下的数染$2$,check是否合法即可。#include<bits/stdc++.h> using namespace std; int T; bool vis[200003]; int main(){ ios::sync_with_stdio(0); for(cin >> T ; T ; --T){ int L; string s; cin >> L >> s; bool getans = 0; for(int i = 0 ; i < 9 && !getans ; ++i){ memset(vis , 0 , sizeof(bool) * (s.size())); int mx = 0 , pre = -1; bool flg = 1; for(int j = 0 ; j < s.size() ; ++j) if(s[j] - '0' <= i){ if(mx > s[j] - '0'){flg = 0; break;} pre = j; mx = max(mx , s[j] - '0'); vis[j] = 1; } if(!flg) continue; for(int j = pre + 1 ; j < s.size() ; ++j) if(s[j] - '0' == i + 1) vis[j] = 1; mx = 0; for(int j = 0 ; j < s.size() ; ++j) if(!vis[j]){ if(mx > s[j] - '0'){flg = 0; break;} mx = s[j] - '0'; } if(flg){ getans = 1; for(int i = 0 ; i < s.size() ; ++i) putchar(vis[i] ? '1' : '2'); } } if(!getans) putchar('-'); putchar('\n'); } return 0; }
D
sol
对于$a=i,b=j$连边$(i,j)$,对于每一个连通块一定是先选择一条边,然后一直选择恰好有一端被标记过的边直到所有点被标记,那么一个连通块的贡献是$sz-1$。并查集实现连通块即可。#include<bits/stdc++.h> using namespace std; const int _ = 1e5 + 7; int N , K , fa[_] , sz[_]; int find(int x){return fa[x] == x ? x : (fa[x] = find(fa[x]));} int main(){ cin >> N >> K; for(int i = 1 ; i <= N ; ++i) sz[fa[i] = i] = 1; for(int i = 1 ; i <= K ; ++i){ int p , q; cin >> p >> q; p = find(p); q = find(q); if(p == q) continue; fa[p] = q; sz[q] += sz[p]; } for(int i = 1 ; i <= N ; ++i) if(fa[i] == i) K -= sz[i] - 1; cout << K; return 0; }
E1
sol
发现在最优情况下只有最大值最大的$n$行有意义,拿出来暴力即可。#include<bits/stdc++.h> using namespace std; int read(){ int a = 0; char c = getchar(); bool f = 0; while(!isdigit(c)){f = c == '-'; c = getchar();} while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return f ? -a : a; } #define PII pair < int , int > int T , N , M , arr[13][2003] , mx[2003] , id[2003]; int mat[5][5] , ans; void dfs(int x){ if(x > N || x > M){ int sum = 0; for(int i = 0 ; i < N ; ++i){ int mx = 0; for(int j = 0 ; j < N ; ++j) mx = max(mx , mat[i][j]); sum += mx; } ans = max(ans , sum); return; } for(int i = 0 ; i < N ; ++i){ for(int j = 0 ; j < N ; ++j) mat[(i + j) % N][x - 1] = arr[j + 1][id[x]]; dfs(x + 1); } } int main(){ for(cin >> T ; T ; --T){ cin >> N >> M; ans = 0; memset(mx , 0 , sizeof(mx)); memset(mat , 0 , sizeof(mat)); for(int i = 1 ; i <= N ; ++i){ for(int j = 1 ; j <= M ; ++j){ cin >> arr[i][j]; mx[j] = max(mx[j] , arr[i][j]); } } for(int i = 1 ; i <= M ; ++i) id[i] = i; sort(id + 1 , id + M + 1 , [&](int A , int B){return mx[A] > mx[B];}); dfs(1); cout << ans << endl; } return 0; }
E2
F
sol
对于每条边以其编号位数为权值跑最短路,建出最短路DAG,那么每个点的答案只有可能由最短路DAG上的路径构成。然后按照DAG拓扑序求出答案。对于每个点找到其在DAG上的所有前驱,两两比较数字串字典序大小。总串长度不长,可以通过建Trie树把每个点对应的串放上去然后求LCA比较字典序。VP的时候没想很多写了个比较麻烦的做法:
对于已求出的每个点确定其最短路转移的唯一前驱,这样就把最短路DAG变成了最短路树。那么二分第一个字符不相同的位置$mid$,然后在最短路树上倍增求出两个字符串的长度为$mid$的前缀的哈希值check,最后通过倍增得到对应位置的两个字符判断哪一个小。
#include<bits/stdc++.h> using namespace std; int read(){ int a = 0; char c = getchar(); bool f = 0; while(!isdigit(c)){f = c == '-'; c = getchar();} while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return f ? -a : a; } #define PII pair < int , int > const int _ = 1e5 + 7 , MOD = 1e9 + 7 , MOD2 = 1e9 + 9; priority_queue < PII > q; vector < PII > Edge[_]; int logg10[_] , dis[_] , N , M , jump[_][20] , val[_] , ans[_]; long long faid[_]; int gethash(int x , int len , int id){ int now; if(len > dis[x]){now = val[x]; len -= dis[x];} else{ for(int i = 19 ; i >= 0 ; --i) if(dis[jump[x][i]] >= len) x = jump[x][i]; now = val[jump[x][0]]; len -= dis[jump[x][0]]; id = faid[x]; } while(logg10[id] != len - 1) id /= 10; for(int i = 0 ; i < len ; ++i) now = 1ll * now * 10 % MOD2; return (now + id) % MOD2; } int getch(int x , int len , int id){ if(len > dis[x]) len -= dis[x]; else{ for(int i = 19 ; i >= 0 ; --i) if(dis[jump[x][i]] >= len) x = jump[x][i]; len -= dis[jump[x][0]]; id = faid[x]; } while(logg10[id] != len - 1) id /= 10; return id % 10; } bool cmp(int p , int q , int idp , int idq){ if(!p) return 1; int L = 1 , R = logg10[idp] + 1 + dis[p]; while(L < R){ int mid = (L + R) >> 1; gethash(p , mid , idp) != gethash(q , mid , idq) ? R = mid : L = mid + 1; } return getch(p , L , idp) > getch(q , L , idq); } void done(int x , int pre , int nid){ ans[x] = ans[pre]; for(int i = 0 ; i <= logg10[nid] ; ++i) ans[x] = ans[x] * 10ll % MOD; val[x] = val[pre]; for(int i = 0 ; i <= logg10[nid] ; ++i) val[x] = val[x] * 10ll % MOD2; ans[x] = (ans[x] + nid) % MOD; val[x] = (val[x] + nid) % MOD2; faid[x] = nid; jump[x][0] = pre; for(int i = 1 ; jump[x][i - 1] ; ++i) jump[x][i] = jump[jump[x][i - 1]][i - 1]; } void dijk(){ memset(dis , 0x3f , sizeof(dis)); dis[0] = dis[1] = 0; q.push(PII(0 , 1)); while(!q.empty()){ PII t = q.top(); q.pop(); if(dis[t.second] != -t.first) continue; int now = 0 , nid = 0; for(auto p : Edge[t.second]) if(dis[p.first] + logg10[p.second] + 1 == dis[t.second]) if(cmp(now , p.first , nid , p.second)){ now = p.first; nid = p.second; } if(now) done(t.second , now , nid); for(auto p : Edge[t.second]) if(dis[p.first] > dis[t.second] + logg10[p.second] + 1){ dis[p.first] = dis[t.second] + logg10[p.second] + 1; q.push(PII(-dis[p.first] , p.first)); } } } int main(){ #ifndef ONLINE_JUDGE freopen("in","r",stdin); freopen("out","w",stdout); #endif N = read(); M = read(); logg10[0] = -1; for(int i = 1 ; i <= M ; ++i) logg10[i] = logg10[i / 10] + 1; for(int i = 1 ; i <= M ; ++i){ int p = read() , q = read(); Edge[p].push_back(PII(q , i)); Edge[q].push_back(PII(p , i)); } dijk(); for(int i = 2 ; i <= N ; ++i) printf("%d\n" , ans[i]); return 0; }
G1
sol
考虑将序列分成若干个极小块,每一块中包含若干种颜色且其他块中不包含这些颜色。那么每一个块的贡献就是块长减去出现次数最多的数字数量。求出这个极小块直接从左往右扫一遍维护当前块已经出现的颜色和如果当前颜色形成极小块块长是多少就可以了。
#include<bits/stdc++.h> using namespace std; int read(){ int a = 0; char c = getchar(); bool f = 0; while(!isdigit(c)){f = c == '-'; c = getchar();} while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return f ? -a : a; } const int _ = 2e5 + 3; int arr[_] , f[_] , nxt[_] , sz[_] , N , Q , ans; vector < int > ch[_]; int main(){ N = read(); Q = read(); for(int i = 1 ; i <= N ; ++i) ++sz[arr[i] = read()]; set < int > in; int cnt = 0 , tar = 0; for(int i = 1 ; i <= N ; ++i){ if(in.find(arr[i]) == in.end()){ in.insert(arr[i]); tar += sz[arr[i]]; } if(tar == ++cnt){ int mx = 0; for(auto t : in) mx = max(mx , sz[t]); ans += tar - mx; in.clear(); cnt = 0; tar = 0; } } cout << ans; return 0; }