第一次打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 6≥6的点一定有解(任取一条从该点出发的回路,至少经过该点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][j−1],否则后面的数都必须转到前面来,每个数需要bbb的代价,转移到F[pj−1−1][j]F[p^{-1}_j-1][j]F[pj−1−1][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;
}