【题解】gym103743 (2022 JSCPC)
A. PENTA KILL!
考虑直接模拟,规则就是一个人将其他人全部都击杀,并且中间没有重复击杀。
code:
#include<bits/stdc++.h> using namespace std; map<string , vector<string> > st; int n; string a,b; int main(){ cin >> n; for(int i = 1 ; i <= n ; ++i){ cin >> a >> b; st[a].push_back(b); } for(auto t : st){ auto e = t.second; for(int i = 4; i < e.size(); ++i){ set<string> s; s.insert(e[i]); s.insert(e[i-1]); s.insert(e[i-2]); s.insert(e[i-3]); s.insert(e[i-4]); if(s.size() == 5){ cout << "PENTA KILL!"; return 0; } } } cout << "SAD:("; }
B. Prime Ring Plus
将
我们考虑可以网络流流流。
我们考虑因为一条边连接的两个点的和是素数,大于
那么连出来的图显然是一个二分图。现在我们的目标就是要让这个二分图中的每一个点有且仅有两个匹配(环中一个点链接两个点)。
我们考虑可以网络流求解,建图方式如下:
- 源点
向所有奇数点连边,流量为 - 奇数点向偶数点(在奇+偶=质数的情况下)连边,流量为
- 偶数点向汇点
连边,流量为

其中橙色边流量为
我们可以发现只要跑出来的网络流满流,那么一定就可以构造出一组解。(中间奇数点向偶数点的边剩余流量为
code:
#include<bits/stdc++.h> using namespace std; const int NN = 4e4 + 8,MM = 6e6 + 8,INF = 0x3f3f3f3f; int n; int S,T; inline int read(){ register char c = getchar(); register int res = 0; while(!isdigit(c)) c = getchar(); while(isdigit(c)) res = res * 10 + c - '0', c = getchar(); return res; } struct Edge{ int to,next,val; }edge[MM << 1]; int head[NN], pre[NN], cnt; void add_edge(int u,int v,int w){ edge[++cnt] = {v,head[u],w}; pre[u] = head[u] = cnt; swap(u,v); edge[++cnt] = {v,head[u],0}; pre[u] = head[u] = cnt; } bool pis[NN]; int prime[NN],cntp; void init(){ memset(head,-1,sizeof(head)); memset(pre,-1,sizeof(pre)); cnt = 1; int m = n * 2; for(int i = 2; i <= m; ++i){ if(!pis[i]) prime[++cntp] = i; for(int j = 1; j <= cntp; ++j){ if(i * prime[j] > m) break; pis[i * prime[j]] = 1; if(i % prime[j] == 0) break; } } } int dis[NN]; bool bfs(){ memset(dis,0,sizeof(dis)); queue<int> q; q.push(S);dis[S] = 1;head[S] = pre[S]; while(!q.empty()){ int u = q.front();q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to,val = edge[i].val; if(val && !dis[v]){ dis[v] = dis[u] + 1; head[v] = pre[v]; q.push(v); if(v == T) return 1; } } } return 0; } int dinic(int u,int flow){ if(u == T) return flow; int res = flow; for(int &i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to,val = edge[i].val; if(val && dis[u] + 1 == dis[v]){ int k = dinic(v,min(flow,val)); if(!k) dis[v] = 0; edge[i].val -= k; edge[i^1].val += k; res -= k; } if(res == 0) break; } return flow - res; } int gt[NN][2]; vector<vector<int> > ansf; bool vis[NN]; int main(){ n = read(); if(n & 1) return puts("-1"),0; init(); S = n+1; T = n+2; for(int i = 1; i <= n; i += 2) add_edge(S,i,2); for(int i = 2; i <= n; i += 2) add_edge(i,T,2); for(int i = 1; i <= n; i += 2) for(int j = 2; j <= n; j += 2) if(!pis[i+j]) add_edge(i,j,1); int ans = 0,flow = 0; while(bfs()){ if(flow = dinic(S,INF)) ans += flow; } if(ans != n) return puts("-1"),0; for(int u = 1; u <= n; u += 2){ head[u] = pre[u]; for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(v == S) continue; if(edge[i].val == 0){ if(!gt[u][0]) gt[u][0] = v; else gt[u][1] = v; if(!gt[v][0]) gt[v][0] = u; else gt[v][1] = u; } } } for(int i = 1; i <= n; i += 2){ if(vis[i]) continue; vector<int> r; int now = i, fm = 0; while(now != i || fm == 0){ int x = now; vis[now] = 1; r.push_back(now); if(fm == gt[now][0]) now = gt[now][1]; else now = gt[now][0]; fm = x; } ansf.push_back(r); } printf("%d\n",ansf.size()); for(int i = 0; i < ansf.size(); ++i){ printf("%d",ansf[i].size()); for(auto j : ansf[i]){ printf(" %d",j); } puts(""); } }
C. Jump and Treasure
DP
待补
D. Finding Pairs
给定一个序列和一个数
,每次询问一个区间,问在区间中找若干对不含重复元素的距离为 的数的最大权值和、
我们考虑
我们对于每一组分别考虑:
我们可以想到一个 DP
思路,设
这个 DP
的转移方程显然是简单的,就是从
然后我们考虑我们的询问显然是不到 DP
会计算很多不需要的状态,我们可以 分块/回滚莫队
解决这道题。
下面是回滚莫队代码(分块没写):
code:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll NN = 1e5 + 8, BB = 4e3 + 8, INF = 1e18; ll n,k,q; ll a[NN],b[NN],c[NN]; inline ll read(){ register ll res = 0,flag = 1; register char c = getchar(); while(!isdigit(c)) flag = c == '-' ? -1 : flag,c = getchar(); while(isdigit(c)) res = res * 10 + c - '0', c = getchar(); return res * flag; } int len,cntb; int block[NN]; struct Query{ int l,r,id; bool operator < (const Query &x) const{ if(block[l] == block[x.l]) return r < x.r; return l < x.l; } }; vector<Query>Q[BB]; ll ans[NN]; ll g[BB][2]; ll f[BB][2][2]; ll solve(int l,int r){ ll res = 0; for(int i = l; i <= r; ++i){ if(i - l < k) g[b[i]][0] = 0,g[b[i]][1] = -INF; ll pre = g[b[i]][0]; res -= g[b[i]][0]; g[b[i]][0] = max(g[b[i]][0],g[b[i]][1] + a[i]); res += g[b[i]][0]; g[b[i]][1] = max(-INF,pre + a[i]); } return res; } ll res; bool vis[NN]; void addl(int x){ if(!vis[b[x]]){ f[b[x]][0][1] = max(-INF,a[x]); f[b[x]][1][0] = max(-INF,a[x]); vis[b[x]] = 1; } else{ ll num0 = f[b[x]][0][0],num1 = f[b[x]][0][1]; res -= f[b[x]][0][0]; f[b[x]][0][0] = max(f[b[x]][0][0],f[b[x]][1][0] + a[x]); res += f[b[x]][0][0]; f[b[x]][1][0] = max(-INF,num0 + a[x]); f[b[x]][0][1] = max(f[b[x]][0][1],f[b[x]][1][1] + a[x]); f[b[x]][1][1] = max(-INF,num1 + a[x]); } } void addr(int x){ if(!vis[b[x]]){ f[b[x]][0][1] = max(-INF,a[x]); f[b[x]][1][0] = max(-INF,a[x]); vis[b[x]] = 1; } else{ ll num0 = f[b[x]][0][0],num1 = f[b[x]][1][0]; res -= f[b[x]][0][0]; f[b[x]][0][0] = max(f[b[x]][0][0],f[b[x]][0][1] + a[x]); res += f[b[x]][0][0]; f[b[x]][0][1] = max(-INF,num0 + a[x]); f[b[x]][1][0] = max(f[b[x]][1][0],f[b[x]][1][1] + a[x]); f[b[x]][1][1] = max(-INF,num1 + a[x]); } } ll sta[NN],num[NN][2][2],nvis[NN],top; int main(){ n = read(); k = read(); q = read(); len = sqrt(n); for(int i = 1; i <= n; ++i) block[i] = (i-1) / k + 1; cntb = block[n]; for(int i = 0; i < k; ++i){ f[i][0][0] = f[i][1][0] = 0; f[i][0][1] = f[i][1][1] = -INF; } for(int i = 1; i <= n; ++i) a[i] = read(),b[i] = i % k,c[i] = (i-1) / k; for(int i = 1; i <= q; ++i){ int l = read(), r = read(); Q[block[l]].push_back({l,r,i}); } for(int i = 1; i <= cntb; ++i) sort(Q[i].begin(),Q[i].end()); for(int i = 1; i <= cntb; ++i){ int r = i * len, l = r + 1; res = 0; for(auto arr : Q[i]){ int ql = arr.l,qr = arr.r; if(block[ql] == block[qr]){ ans[arr.id] = solve(ql,qr); continue; } while(r < qr) addr(++r); ll x = res; for(int j = ql; j <= l; ++j) sta[++top] = b[j], num[top][0][0] = f[b[j]][0][0], num[top][0][1] = f[b[j]][0][1], num[top][1][0] = f[b[j]][1][0], num[top][1][1] = f[b[j]][1][1], nvis[top] = vis[b[j]]; while(l > ql) addl(--l); ans[arr.id] = res; while(top){ f[sta[top]][0][0] = num[top][0][0], f[sta[top]][0][1] = num[top][0][1], f[sta[top]][1][0] = num[top][1][0], f[sta[top]][1][1] = num[top][1][1]; vis[sta[top]] = nvis[top]; --top; } res = x;l = i * len + 1; } for(int j = (i-1)*len+1; j <= i*len; ++j) f[b[j]][0][0] = 0, f[b[j]][1][0] = f[b[j]][0][1] = f[b[j]][1][1] = -INF, vis[b[j]] = 0; } for(int i = 1; i <= q; ++i){ printf("%lld\n",ans[i]); } }
E Playing Cards
Alice
和Bob
在玩游戏,每个人都有张牌,每张牌上有一个数字 ,每轮游戏双方都分别出一张牌,最后点数大的一方获胜。 现在
Alice
知道Bob
的出牌顺序,并且Alice
有魔法可以让一张牌的点数,求 最少需要施展多少次魔法才能在每一局中获胜? 输出魔法次数和方案
我们考虑如果说 Alice
施展完魔法,那么 Alice
的出牌顺序一定是和 Bob
出的牌的大小位次相同(即若 Bob
从大到小出牌,那么 Alice
也从大到小出牌)
我们考虑先对 Bob
的牌从小到大排序。
我们考虑有 Bob
的所有牌,如果说当前 Alice
的最小的牌比 Bob
的小,那么就施展魔法知道刚好超过 Bob
的这张牌,我们再重新对 Alice
的牌排序,继续做这个操作,直到 Alice
的牌更大,那么我们就可以将这两个牌分别移除。
我们考虑上面的做法为什么会这么劣,发现因为有可能一张 Alice
的牌可能会一直被施展魔法,直到最后最劣被施展
我们考虑如何一步到位,我们考虑对于 Bob
的第 Alice
所有小于
我们会发现,这个堆中的所有数如果和
然后我们每次再在堆中找到浪费点数最小的牌,具体地,可以将牌 lower_bound()
进行查询即可,然后再通过堆中记录的对应下标计算需要使用的魔法次数即可。
注:代码的思路与上文相同,但是将问题反了一下,变成了从大到小匹配,一次魔法给 Bob
的牌
code:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll NN = 1e5 + 8, BB = 4e3 + 8, INF = 1e18; ll n,k,q; ll a[NN],b[NN],c[NN]; inline ll read(){ register ll res = 0,flag = 1; register char c = getchar(); while(!isdigit(c)) flag = c == '-' ? -1 : flag,c = getchar(); while(isdigit(c)) res = res * 10 + c - '0', c = getchar(); return res * flag; } int len,cntb; int block[NN]; struct Query{ int l,r,id; bool operator < (const Query &x) const{ if(block[l] == block[x.l]) return r < x.r; return l < x.l; } }; vector<Query>Q[BB]; ll ans[NN]; ll g[BB][2]; ll f[BB][2][2]; ll solve(int l,int r){ ll res = 0; for(int i = l; i <= r; ++i){ if(i - l < k) g[b[i]][0] = 0,g[b[i]][1] = -INF; ll pre = g[b[i]][0]; res -= g[b[i]][0]; g[b[i]][0] = max(g[b[i]][0],g[b[i]][1] + a[i]); res += g[b[i]][0]; g[b[i]][1] = max(-INF,pre + a[i]); } return res; } ll res; bool vis[NN]; void addl(int x){ if(!vis[b[x]]){ f[b[x]][0][1] = max(-INF,a[x]); f[b[x]][1][0] = max(-INF,a[x]); vis[b[x]] = 1; } else{ ll num0 = f[b[x]][0][0],num1 = f[b[x]][0][1]; res -= f[b[x]][0][0]; f[b[x]][0][0] = max(f[b[x]][0][0],f[b[x]][1][0] + a[x]); res += f[b[x]][0][0]; f[b[x]][1][0] = max(-INF,num0 + a[x]); f[b[x]][0][1] = max(f[b[x]][0][1],f[b[x]][1][1] + a[x]); f[b[x]][1][1] = max(-INF,num1 + a[x]); } } void addr(int x){ if(!vis[b[x]]){ f[b[x]][0][1] = max(-INF,a[x]); f[b[x]][1][0] = max(-INF,a[x]); vis[b[x]] = 1; } else{ ll num0 = f[b[x]][0][0],num1 = f[b[x]][1][0]; res -= f[b[x]][0][0]; f[b[x]][0][0] = max(f[b[x]][0][0],f[b[x]][0][1] + a[x]); res += f[b[x]][0][0]; f[b[x]][0][1] = max(-INF,num0 + a[x]); f[b[x]][1][0] = max(f[b[x]][1][0],f[b[x]][1][1] + a[x]); f[b[x]][1][1] = max(-INF,num1 + a[x]); } } ll sta[NN],num[NN][2][2],nvis[NN],top; int main(){ n = read(); k = read(); q = read(); len = sqrt(n); for(int i = 1; i <= n; ++i) block[i] = (i-1) / k + 1; cntb = block[n]; for(int i = 0; i < k; ++i){ f[i][0][0] = f[i][1][0] = 0; f[i][0][1] = f[i][1][1] = -INF; } for(int i = 1; i <= n; ++i) a[i] = read(),b[i] = i % k,c[i] = (i-1) / k; for(int i = 1; i <= q; ++i){ int l = read(), r = read(); Q[block[l]].push_back({l,r,i}); } for(int i = 1; i <= cntb; ++i) sort(Q[i].begin(),Q[i].end()); for(int i = 1; i <= cntb; ++i){ int r = i * len, l = r + 1; res = 0; for(auto arr : Q[i]){ int ql = arr.l,qr = arr.r; if(block[ql] == block[qr]){ ans[arr.id] = solve(ql,qr); continue; } while(r < qr) addr(++r); ll x = res; for(int j = ql; j <= l; ++j) sta[++top] = b[j], num[top][0][0] = f[b[j]][0][0], num[top][0][1] = f[b[j]][0][1], num[top][1][0] = f[b[j]][1][0], num[top][1][1] = f[b[j]][1][1], nvis[top] = vis[b[j]]; while(l > ql) addl(--l); ans[arr.id] = res; while(top){ f[sta[top]][0][0] = num[top][0][0], f[sta[top]][0][1] = num[top][0][1], f[sta[top]][1][0] = num[top][1][0], f[sta[top]][1][1] = num[top][1][1]; vis[sta[top]] = nvis[top]; --top; } res = x;l = i * len + 1; } for(int j = (i-1)*len+1; j <= i*len; ++j) f[b[j]][0][0] = 0, f[b[j]][1][0] = f[b[j]][0][1] = f[b[j]][1][1] = -INF, vis[b[j]] = 0; } for(int i = 1; i <= q; ++i){ printf("%lld\n",ans[i]); } }
F. Pockets
有
种物品和一个容纳重量为 的背包,每种物品有价值 和重量 两种属性,无限个.
你可以购买至多次,每次购买一个任意种物品,如果购买了 个,最后重量不能超过 .
一种方案的价值是物品价值之积,求所有方案价值之和.
我们考虑一次购买的生成函数:
我们枚举买的次数
本文来自博客园,作者:ricky_lin,转载请注明原文链接:https://www.cnblogs.com/rickylin/p/17966334/gym103743
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2023-01-15 BSGS&exBSGS
2023-01-15 exgcd 及其应用