正题
A. Yellow Cards
最大的肯定就是每一个球员依次黄牌。
最小的就是将每个球员都恰好先罚剩一张,再一直罚。
#include<bits/stdc++.h>
using namespace std;
int n,a1,a2,k1,k2;
int main(){
scanf("%d %d %d %d %d",&a1,&a2,&k1,&k2,&n);
int temp=max(n-a1*(k1-1)-a2*(k2-1),0);
printf("%d ",temp);
if(k1>k2) swap(k1,k2),swap(a1,a2);
if(n>a1*k1) printf("%d",a1+(n-a1*k1)/k2);
else printf("%d",n/k1);
}
B. The Number of Products
跟ai的值无关,我们把-1的点标出来,然后枚举-1的位置算答案就可以了。
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;
const int N=200010;
int n;
int a[N];
vector<int> f;
int g[2];
int main(){
scanf("%d",&n);
f.push_back(0);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]<0?f.push_back(i),1:0;
f.push_back(n+1);
int now=0;
long long ans1=0,ans2=0;
for(int i=1;i<f.size();i++){
long long temp=f[i]-f[i-1]-1;
ans2+=(1+temp)*temp/2;
}
for(int i=f.size()-2;i>0;i--){
g[now]+=f[i+1]-f[i];
ans1+=1ll*(f[i]-f[i-1])*g[now];
ans2+=1ll*(f[i]-f[i-1])*g[now^1];
now^=1;
}
printf("%I64d %I64d",ans1,ans2);
}
C. Swap Letters
只有两种对(a,b),(b,a)
两个相同的对交换一次就可以。不同两个对最多一对。
记得判断-1即可。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const int N=200010;
int n;
char a[N],b[N];
int f[N],g[N];
int t1,t2;
int main(){
scanf("%d",&n);
scanf("%s",a+1);
scanf("%s",b+1);
for(int i=1;i<=n;i++) if(a[i]!=b[i]){
if(a[i]=='a') f[++t1]=i;
else g[++t2]=i;
}
if((t1+t2)&1) printf("-1");
else{
if(t1&1) printf("%d\n",t1/2+t2/2+2);
else printf("%d\n",t1/2+t2/2);
for(int i=2;i<=t1;i+=2) printf("%d %d\n",f[i],f[i-1]);
for(int i=2;i<=t2;i+=2) printf("%d %d\n",g[i],g[i-1]);
if(t1&1){
printf("%d %d\n",f[t1],f[t1]);
printf("%d %d\n",f[t1],g[t2]);
}
}
}
D. Ticket Game
有用的只有四个东西,左边的和,右边的和,左边的问号个数,右边的问号个数
如果一边的值和问号比另一边都严格小,那么先手必胜。
否则就在值小的那边填问号,若差为9的倍数,而且问号个数为偶数,那么后手必胜,否则还是先手必胜。
#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;
const int N=200010;
int n;
char s[N];
int main(){
scanf("%d",&n);
scanf("%s",s+1);
int l=0,r=0,le=0,ri=0;
for(int i=1;i<=n/2;i++) s[i]=='?'?le++:l+=s[i]-'0';
for(int i=n/2+1;i<=n;i++) s[i]=='?'?ri++:r+=s[i]-'0';
if(le>ri) swap(le,ri),swap(l,r);
ri-=le;l-=r;
if(l<0 || ri%2==1 || l%9 || l/9!=ri/2) printf("Monocarp");
else printf("Bicarp");
}
E. Marbles
处理出来两两交换的次数。
然后直接暴力Dp转移就可以了。
#include <ctime>
#include <cstdio>
#include <random>
#include <algorithm>
#include <ctime>
#include <iostream>
#include <queue>
#include <bitset>
#include <string>
#include <cstring>
#include <cmath>
#define log lg
#include <unordered_map>
using namespace std;
const int N = 1e6;
int n;
vector<int>g[21];
int log[1<<20];
long long f[1<<20], a[20][20];
int main() {
scanf("%d", &n);
for (int i = 0; i < 20; i ++)
log[1<<i] = i;
for (int i = 1; i <= n ; i ++){
int x;
scanf("%d", &x);
g[x - 1].push_back(i);
}
for (int i = 0; i < 20; i ++)
for (int j = 0; j < 20; j ++)
if (i != j && g[i].size() && g[j].size()) {
int pos2 = 0;
for (int pos1 = 0; pos1 < g[i].size(); pos1 ++){
while (pos2 < g[j].size() - 1 && g[j][pos2 + 1] < g[i][pos1]) pos2 ++;
if (g[i][pos1] > g[j][pos2])
a[i][j] += pos2 + 1;
}
}
for (int i = 1; i < (1 << 20); i ++)
if ((i&-i) != i)
f[i] = 1e18;
for (int i = 1;i < (1 << 20); i ++){
int x = i ^ ((1 << 20) - 1);
while (x) {
int xx = x & -x;
int y = i;
long long v = f[i];
while (y) {
v += a[log[y & -y]][log[xx]];
y -= y & -y;
}
f[xx + i] = min(f[xx + i], v);
x -= xx;
}
}
cout<<f[(1<<20) - 1];
return 0;
}
F. Radio Stations
最后一题题面太长了。
这道题如果没有功率的限制,显然就是一个裸的2-sat
考虑将功率的限制也放在图上:如果选择了功率i,那么功率区间不包含它的点只能不选,连边即可
但是这样建图的边数是o(n^2),需要优化
将功率区间分为两种,一种在这个点前面,另一种在这个点的后面
同样将功率也裂成两个点,分别连向这两种区间,因为功率i的第1种点一定是功率i+1的第一种点的子集,因此(i+1)1->i1和功率右区间恰好在i+1的点,同理(i-1)2->i2和功率左区间恰好为i-1的点
对图求强连通,如果裂成的两个点中在同一个强连通就不行,否则所有选1的区间都要选,功率是最大的i2
#include<bits/stdc++.h>
using namespace std;
#define N 1600005
struct ji{
int nex,to;
}edge[N<<2];
int E,n,m,m1,m2,x,y,ans,head[N],vis[N],dfn[N],low[N],s[N];
void add(int x,int y){
edge[E].nex=head[x];
edge[E].to=y;
head[x]=E++;
}
void dfs(int k){
dfn[k]=low[k]=++x;
s[++s[0]]=k;
for(int i=head[k];i!=-1;i=edge[i].nex){
int v=edge[i].to;
if (!dfn[v]){
dfs(v);
low[k]=min(low[k],low[v]);
}
else
if (!vis[v])low[k]=min(low[k],dfn[v]);
}
if (dfn[k]==low[k]){
vis[k]=++vis[0];
while (s[s[0]]!=k)vis[s[s[0]--]]=vis[0];
s[0]--;
}
}
int main(){
scanf("%d%d%d%d",&m1,&n,&m,&m2);
memset(head,-1,sizeof(head));
for(int i=1;i<=m1;i++){
scanf("%d%d",&x,&y);
add(2*x-1,2*y);
add(2*y-1,2*x);
}
for(int i=1;i<=n;i++){
scanf("%d%d",&x,&y);
add(2*i,2*n+2*x);
add(2*n+2*x-1,2*i-1);
if (y<m){
add(2*i,2*n+2*y+1);
add(2*n+2*y+2,2*i-1);
}
}
for(int i=1;i<=m2;i++){
scanf("%d%d",&x,&y);
add(2*x,2*y-1);
add(2*y,2*x-1);
}
for(int i=1;i<m;i++){
add(2*n+2*i+2,2*n+2*i);
add(2*n+2*i-1,2*n+2*i+1);
}
x=0;
for(int i=2;i<=2*(n+m);i+=2)
if (!dfn[i])dfs(i);
for(int i=1;i<=2*(n+m);i+=2)
if (!dfn[i])dfs(i);
for(int i=1;i<=n+m;i++)
if (vis[2*i-1]==vis[2*i]){
printf("-1");
return 0;
}
for(int i=1;i<=n;i++)ans+=(vis[2*i]<vis[2*i-1]);
for(int i=m;i;i--)
if (vis[2*n+2*i]<vis[2*n+2*i-1]){
printf("%d %d\n",ans,i);
for(int j=1;j<=n;j++)
if (vis[2*j]<vis[2*j-1])printf("%d ",j);
return 0;
}
}
本文深入解析算法竞赛中的关键策略,包括最大最小化问题、产品数量计算、字母交换、游戏策略、强连通分量分析及2-SAT问题的扩展。通过具体题目示例,展示如何运用编程技巧解决复杂算法挑战。
311

被折叠的 条评论
为什么被折叠?



