发现已经错过最美的花期|

ricky_lin

园龄:3年8个月粉丝:11关注:2

【题解】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

1n 分成若干个环,每个环上相邻两数之和是质数

我们考虑可以网络流流流。

我们考虑因为一条边连接的两个点的和是素数,大于 2 的素数都是奇数,所以说一条边链接的两个数一定是一奇一偶。

那么连出来的图显然是一个二分图。现在我们的目标就是要让这个二分图中的每一个点有且仅有两个匹配(环中一个点链接两个点)。

我们考虑可以网络流求解,建图方式如下:

  • 源点 S 向所有奇数点连边,流量为 2
  • 奇数点向偶数点(在奇+偶=质数的情况下)连边,流量为 1
  • 偶数点向汇点 T 连边,流量为 2

其中橙色边流量为 2,绿色边流量为 1

我们可以发现只要跑出来的网络流满流,那么一定就可以构造出一组解。(中间奇数点向偶数点的边剩余流量为 0,那么一定被选作了环中的其中一条边)

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

给定一个序列和一个数 k,每次询问一个区间,问在区间中找若干对不含重复元素的距离为 k 的数的最大权值和、

我们考虑 modk 不同的位置互相不影响,所以说我们可以先把所有位置按 modk 分组。

我们对于每一组分别考虑:

我们可以想到一个 DP 思路,设 fi,j,0/1,0/1 表示区间 [i,j] 最左边是否取,最右边是否取,得到的最大权值和

这个 DP 的转移方程显然是简单的,就是从 [i,j1][i+1,j] 转移一下即可,具体可以看代码。

然后我们考虑我们的询问显然是不到 n2 的,所以 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

AliceBob 在玩游戏,每个人都有 n 张牌,每张牌上有一个数字 x,每轮游戏双方都分别出一张牌,最后点数大的一方获胜。

现在 Alice 知道 Bob 的出牌顺序,并且 Alice 有魔法可以让一张牌的点数 +k,求 Alice 最少需要施展多少次魔法才能在每一局中获胜?

输出魔法次数和方案

我们考虑如果说 Alice 施展完魔法,那么 Alice 的出牌顺序一定是和 Bob 出的牌的大小位次相同(即若 Bob 从大到小出牌,那么 Alice 也从大到小出牌)

我们考虑先对 Bob 的牌从小到大排序。

我们考虑有 n2logn 的做法,就是说我们从小到大去赢 Bob 的所有牌,如果说当前 Alice 的最小的牌比 Bob 的小,那么就施展魔法知道刚好超过 Bob 的这张牌,我们再重新对 Alice 的牌排序,继续做这个操作,直到 Alice 的牌更大,那么我们就可以将这两个牌分别移除。

我们考虑上面的做法为什么会这么劣,发现因为有可能一张 Alice 的牌可能会一直被施展魔法,直到最后最劣被施展 n 次魔法。

我们考虑如何一步到位,我们考虑对于 Bob 的第 i 张牌 bi,我们将 Alice 所有小于 bi+k1 的没有被删除的牌加入一个堆。

我们会发现,这个堆中的所有数如果和 bi 配对,浪费的点数不会超过 k1,不在堆中的书显然会更劣。

然后我们每次再在堆中找到浪费点数最小的牌,具体地,可以将牌 ai 在推入堆时放入二元组 (aimodk,i),然后在查询时 lower_bound() 进行查询即可,然后再通过堆中记录的对应下标计算需要使用的魔法次数即可。

注:代码的思路与上文相同,但是将问题反了一下,变成了从大到小匹配,一次魔法给 Bob 的牌 k

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

n 种物品和一个容纳重量为 k 的背包,每种物品有价值 v 和重量 w 两种属性,无限个.
你可以购买至多 m 次,每次购买一个任意种物品,如果购买了 i 个,最后重量不能超过 k+i.
一种方案的价值是物品价值之积,求所有方案价值之和 mod998244353.

我们考虑一次购买的生成函数:

f(x)=vixwi

我们枚举买的次数 i,可以得到答案:

i=0mj=0i+k[xj]fi(x)

posted @   ricky_lin  阅读(39)  评论(0编辑  收藏  举报
历史上的今天:
2023-01-15 BSGS&exBSGS
2023-01-15 exgcd 及其应用
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 有我 周深
有我 - 周深
00:00 / 00:00
An audio error has occurred.

作词 : 唐恬/闫光宇

作曲 : 钱雷

编曲 : 赵兆/付虹宇

制作人 : 赵兆

出品 : 共青团中央宣传部

版权 : 中国青少年新媒体协会

制作单位 : 能量悦动音乐

发行单位 : 银河方舟StarNation

出品人 : 郭峰

总监制 : 汤杰

总策划 : 钟亚楠

总统筹 : 金慧子

音乐监制 : 李天鹏/李三木

制作执行 : 张不贰/高聪怡

项目宣发 : 肖健/张国党/孙小千/戴胤/孙雯璟

音乐推广 : 代诗琪/杜思潮/马越/程铁峰/傅之豪

钢琴 : 赵兆

吉他 : 伍凌枫

贝斯 : 韩阳

鼓 : 武勇恒

合唱设计 : 赵兆

合唱 : 凡尔赛合唱团

人声录音 : 耿潇微

人声录音室 : 55TEC Studio Beijing

配唱 : 徐威@52Hz Studio (Shanghai)

混音 : 李游(小骷髅)@55TEC Studio Beijing

海报 : 格子

特别鸣谢 : 周深工作室

世界问 你是谁 来自哪 请回答

爱什么 梦什么 去何方 请回答

答案有 一百年的时光

我来自 硝烟中 课桌旁 的太阳

我来自 硝烟中 课桌旁 的太阳

他和她 宣的誓 迎的仗

来自那 燃烧的 和我一样 的年华

来自世间 一对平凡的夫妻 身旁

来自世间 一对平凡的夫妻 身旁

来自昨天 谁以青春赴万丈 理想

我是寸土 不让的 家乡啊

我是绝不 低头的 倔强啊

接过万千热血 的初衷

当有对答世界 的音量

要怎么形容明天 像我一样

要怎么形容明天 像我一样

承风骨亦有锋芒 有梦则刚

去何方 去最高 的想象

前往皓月星辰 初心不忘

那未来如何登场 有我担当

那未来如何登场 有我担当

定是你只能叫好 那种辉光

护身旁 战远方 有我啊

我的名字就是 站立的地方

Wu~

我的样子 就是 明天的模样

我是朝阳 落在乡间听书声 朗朗

我是朝阳 落在乡间听书声 朗朗

我是屏障 为谁挡一程厄运 的墙

我要一生 清澈地 爱着啊

我要长歌 领着风 踏着浪

朝着星辰大海 的方向

当有对答世界 的音量

要怎么形容明天 像我一样

要怎么形容明天 像我一样

承风骨亦有锋芒 有梦则刚

去远方 去最高 的想象

前往皓月星辰 初心不忘

那未来如何登场 有我担当

那未来如何登场 有我担当

定是你只能叫好 那种辉光

护身旁 战远方 有我啊

一生骄傲为我 站立的地方

Wu~

我的样子 就是 中国的模样

Wu~~~ Wu~~~

当炬火 去化作那道光

“谨以此歌献给一代代不负时代重托的中国青年”

“谨以此歌献给一代代不负时代重托的中国青年”