Atcoder Grand Contest 032 题解&游记

作者首次参加AGC比赛,面对难度较高的题目感到不适应。通过冷静思考解决了部分问题,分享了解题思路,包括有限插入、平衡邻居、三个电路及旋转排序等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一次打agc,感觉并不习惯赛制,被打爆了。
题目都好难啊。家里的网奇差,点一下玩一年。
开场看A,不会。
看B,不会。
自闭了。
冷静了10min,大概会了B,写了一发,还写挂了。
自闭了。
再看A,再冷静了10min,大概会了,写了一发,过了。这时已经过了0.5h。
自闭了。
跟榜开了D,口胡了个区间dp,写了一发,挂了。冷静一下,感觉是做法假了,然而并不会靠谱做法。
自闭了。
只能开C,想了会以为判判点度就对了,结果刚想敲发现假了。
自闭了。
冷静了一会,发现如果存在三个度数为4的点一定可行,于是瞎判判就好了。写了一发,造个数据调了好久。
自闭了。
再看D,凭信仰换了一个dp,这次感觉能证明正确性了。写了一发,又调了好久。结果过了只剩10min了,罚时爆炸。
自闭弃疗了。
想了一下E,大概会了,然而end了。被神仙yyb摁在地上锤。

简要题解
A Limited Insertion

被sb题锤爆了。考虑时间倒流,最后一次操作一定会加入一个在最终序列中满足bi=ib_i=ibi=i的点,有多个的话只能删最后一个,还要看看是否合法。

#include <bits/stdc++.h>
 
using namespace std;
 
int num[105],ans[105];
bool in[105];
 
int main() {
  memset(in,1,sizeof(in));
  int n;
  scanf("%d",&n);
  for(int i=1;i<=n;i++) scanf("%d",&num[i]);
  for(int i=n;i>0;i--) {
  	int cur=0,s=0;
    for(int j=1;j<=n;j++)
      if (in[j]) {
      	s++;
      	if (num[j]==s) cur=j;
	  }
	if (cur&&num[cur]<=i) {
		ans[i]=num[cur];
		in[cur]=0;
	}
	else {
		puts("-1");
		return 0;
	}
  }
  for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
  return 0;
}
B Balanced Neighbors

考虑构造一个划分,满足每个部分的和相等,这样只要让每个点跟和自己不在同一个部分的点连边就合法了。
怎么构造呢?考虑∑i=1ni=n(n+1)2\sum_{i=1}^{n}i=\frac{n(n+1)}{2}i=1ni=2n(n+1),若nnn是奇数每块和为n+1n+1n+1,两两匹配,否则和为nnn,多出一个nnn单独一块。

#include <bits/stdc++.h>
#define FR first
#define SE second

using namespace std;

typedef pair<int,int> pr;

vector <pr> ans;

int main() {
  int n;
  scanf("%d",&n);
  int m=((n&1)?n:n+1);
  for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)
      if (i+j!=m) ans.push_back(pr(i,j));
  printf("%d\n",ans.size());
  for(int i=0;i<ans.size();i++) printf("%d %d\n",ans[i].FR,ans[i].SE);
  return 0;
}
C Three Circuits

这题是个大讨论题。
因为图连通且每条边恰经过一次,所以三个环可以拼成一个欧拉回路,因此可以尝试从欧拉回路的角度考虑。
原图必须存在欧拉回路,有奇点显然无解。
有度数≥6\geq 66的点一定有解(任取一条从该点出发的回路,至少经过该点333次,每次即为一个环)。
否则若度数全为222或仅有一点度数为444显然无解。
还可以发现若存在至少333个度数为444的点也一定有解(设三个点为A,B,C,同样任取一条点A出发的欧拉回路,若出现环套环就可以构造,否则一定形如A->B->C->A->B->C->A,可以构造出A->B->A,B->C->B,C->A->C的三个环)。
那现在只剩恰有两个度为444的点的情况,这种情况下可以将度为222的点缩起来,有解当且仅当存在自环。

#include <bits/stdc++.h>
 
using namespace std;
 
struct Edge {
  int t,next;
  Edge() {}
  Edge(int a,int b):t(a),next(b) {}
};
 
Edge e[200005];
int head[100005],d[100005],rt1,rt2;
 
void dfs(int x,int fa) {
  for(int i=head[x];i;i=e[i].next)
    if (e[i].t!=fa) {
    	int u=e[i].t;
    	if (d[u]==2) dfs(u,x);
    	else if (u==rt1) {
    		puts("Yes");
    		exit(0);
		}
	}
}
 
int main() {
  int n,m;
  scanf("%d%d",&n,&m);
  for(int i=1;i<=m;i++) {
  	int x,y;
  	scanf("%d%d",&x,&y);
  	e[2*i-1]=Edge(y,head[x]);
  	head[x]=2*i-1;
  	e[2*i]=Edge(x,head[y]);
  	head[y]=2*i;
  	d[x]++;d[y]++;
  }
  for(int i=1;i<=n;i++)
    if (d[i]&1) {
    	puts("No");
    	return 0;
	}
  int cnt=0;
  for(int i=1;i<=n;i++)
    if (d[i]>=6) {
    	puts("Yes");
    	return 0;
	}
	else if (d[i]==4) cnt++;
  if (cnt<2) {
  	puts("No");
  	return 0;
  }
  if (cnt>2) {
  	puts("Yes");
  	return 0;
  }
  for(int i=1;i<=n;i++)
    if (d[i]==4) {
    	if (!rt1) rt1=i; else rt2=i;
	}
  dfs(rt1,0);
  puts("No");
  return 0;
}
D Rotation Sort

这题有个一眼的dp式,设F[i][j]F[i][j]F[i][j]表示将原序列中权值在[i,j][i,j][i,j]中的点排序的最小代价,转移直接枚举一下左右端点。
很可惜是假的,可以被5 1 2 3 4 7 8 9 10 6这种数据卡掉。
考虑靠谱的做法,每次旋转只会改变一个数和其他数的相对位置,所以如果改变一个数的相对位置,一定会改到最终的位置。
那么设F[i][j]F[i][j]F[i][j]表示将原序列前iii个数中不超过jjj的数字排序的最小代价。
考虑最大值jjj,若它在最后就不用动,否则有两种选择,第一种是直接花aaa的代价放到最后,转移到F[i][j−1]F[i][j-1]F[i][j1],否则后面的数都必须转到前面来,每个数需要bbb的代价,转移到F[pj−1−1][j]F[p^{-1}_j-1][j]F[pj11][j]

#include <bits/stdc++.h>
 
using namespace std;
 
typedef long long ll;
 
int num[5005],pos[5005],cnt[5005];
ll f[5005][5005];
 
int main() {
  memset(f,0x3f,sizeof(f));
  int n,a,b;
  scanf("%d%d%d",&n,&a,&b);
  for(int i=1;i<=n;i++) {
  	scanf("%d",&num[i]);
  	pos[num[i]]=i;
  }
  for(int i=0;i<=n;i++) f[i][0]=f[0][i]=0;
  for(int i=1;i<=n;i++) {
  	for(int j=num[i]+1;j<=n;j++)
  	  if (pos[j]<i) cnt[j]++;
    for(int j=1;j<=n;j++)
      if (pos[j]<=i) {
      	if (!cnt[j]) f[i][j]=f[i][j-1];
      	else f[i][j]=min(f[i][j-1]+a,f[pos[j]-1][j]+(ll)b*cnt[j]);
	  }
      else f[i][j]=f[i][j-1];
  }
  printf("%lld\n",f[n][n]);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值