AtCoder Grand Contest 032
Preface
这场前面题目巨简单啊,1h把ABCD都做了,E题猜了个结论看了题解就是对的,F太难不会
A - Limited Insertion
正着做很难考虑,我们考虑倒着处理
当\(a_i=i\)时显然这个位置可以被删去,我们发现如果有多个位置先删除后面的肯定不会更劣
直接\(O(n^2)\)模拟即可
#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=105;
int n,tn,a[N],ans[N];
int main()
{
RI i,j; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
tn=n; while (n)
{
int pos=0; for (i=n;i;--i) if (a[i]==i) { pos=i; break; }
if (!pos) return puts("-1"),0; ans[n]=pos;
for (i=pos;i<n;++i) a[i]=a[i+1]; --n;
}
for (i=1;i<=tn;++i) printf("%d\n",ans[i]); return 0;
}
B - Balanced Neighbors
首先考虑让图联通,不难想到直接把所有边都连起来,然后删去一些边
然后很显然,我们可以分奇偶数讨论一下:
- 若\(n\)为奇数,可以删去边\((1,n-1),(2,n-2),\cdots\)
- 若\(n\)为偶数,可以删去边\((1,n),(2,n-1),\cdots\)
正确性十分显然
#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=105;
int n,m; bool g[N][N];
int main()
{
RI i,j; for (scanf("%d",&n),i=1;i<=n;++i)
for (j=i+1;j<=n;++j) g[i][j]=1;
for (i=1;i<n-i+!(n&1);++i) g[i][n-i+!(n&1)]=0;
for (i=1;i<=n;++i) for (j=i+1;j<=n;++j) if (g[i][j]) ++m;
for (printf("%d\n",m),i=1;i<=n;++i) for (j=i+1;j<=n;++j)
if (g[i][j]) printf("%d %d\n",i,j); return 0;
}
C - Three Circuits
首先我们发现题目中要求的是欧拉回路,那么首先每个点度数都必须是偶数
然后这题目我们显然是准备分类讨论,根据样例很容易看出若存在一个度数\(\ge 6\)的点,显然可以经过这个点把欧拉回路分成\(3\)部分
那么现在每个点度数就是\(2,4\),我们记度数为\(4\)的点有\(c_4\)个,考虑若\(c_4\ge 3\),画画图就会发现此时要么组成大环要么就是断开成小环必然是合法的
同时我们发现\(c_4=1\)的情况显然是不合法的,那么以下考虑\(c_4=2\)的情况
因为这时显然我们可以把原来度数为\(2\)的点缩起来,那么考虑两点之间要么是两个端点的关系,要么就是在路径上
我们发现前者两点间有四条路径,这样显然是不合法的,而后者两点间仅有两条路径,容易发现可以构造出合法方案
直接爆搜判断下即可,复杂度\(O(n+m)\)
#include<cstdio>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
struct edge
{
int to,nxt;
}e[N<<1]; int n,m,head[N],cnt,x,y,deg[N],c4,num; bool vis[N];
inline void addedge(CI x,CI y)
{
e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
e[++cnt]=(edge){x,head[y]}; head[y]=cnt;
}
#define to e[i].to
inline void DFS(CI now)
{
if (now==y) return (void)(++num);
for (RI i=head[now];i;i=e[i].nxt)
if (!vis[i+1>>1]) vis[i+1>>1]=1,DFS(to);
}
#undef to
int main()
{
RI i; for (scanf("%d%d",&n,&m),i=1;i<=m;++i)
scanf("%d%d",&x,&y),addedge(x,y),++deg[x],++deg[y];
for (i=1;i<=n;++i) if (deg[i]&1) return puts("No"),0;
for (i=1;i<=n;++i) if (deg[i]>=6) return puts("Yes"),0;
for (i=1;i<=n;++i) if (deg[i]==4) ++c4;
if (c4>=3) return puts("Yes"),0; if (c4<=1) return puts("No"),0;
for (i=1;i<=n;++i) if (deg[i]==4) { x=i; break; }
for (i=x+1;i<=n;++i) if (deg[i]==4) { y=i; break; }
return DFS(x),puts(num==4?"No":"Yes"),0;
}
D - Rotation Sort
陈指导一眼秒掉ORZ,不过确实是挺naive的
我们考虑旋转操作的本质是什么,容易发现两个操作其实就是选择一个数,花费一定的代价将其插入到左侧或者右侧任一位置
因为我们若移动了一个数是一定可以把它移到该去的位置的,因此容易发现我们可以对没有移动的数进行DP
具体地,设\(f_{i,j}\)考虑前\(i\)个数,最后一个没有被移动的数为\(j\)时最小的代价,转移的时候分\(a_i>j\)和\(a_i<j\)讨论即可
复杂度\(O(n^2)\),可以轻松通过
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=5005;
const long long INF=1e18;
int n,a,b,x; long long f[N][N],ans=INF;
int main()
{
RI i,j; for (scanf("%d%d%d",&n,&a,&b),i=1;i<=n;++i)
for (j=1;j<=n;++j) f[i][j]=INF; for (i=1;i<=n;++i)
for (scanf("%d",&x),j=1;j<=n;++j)
if (x<j) f[i][j]=min(f[i][j],f[i-1][j]+b); else
f[i][x]=min(f[i][x],f[i-1][j]),f[i][j]=min(f[i][j],f[i-1][j]+a);
for (i=1;i<=n;++i) ans=min(ans,f[n][i]);
return printf("%lld",ans),0;
}
E - Modulo Pairing
那个结论还是挺好猜的说,但是证明的讨论还是太懒了不想分类……
首先我们对整个序列进行排序,可以证明所有解都可以调整成如下形式:
- 存在一个分界点\(p\)
- 对于\([1,p]\)中的元素按照\((1,p),(2,p-1),\cdots\)的形式匹配,且满足各组的和小于\(M\)
- 对于\([p+1,2n]\)中的元素按照\((p+1,2n),(p+2,2n-1),\cdots\)的形式匹配,且满足各组的和不小于\(M\)
考虑如何证明这个调整的正确性,我们讨论一下(以下证明来自 cz_xuyixuan聚聚的博客)
若令蓝线表示一组和小于\(M\)的匹配,红线表示一组和不小于\(M\)的匹配,按照下图的方式调整可以保证方案不会变劣
然后我们发现当分界点\(p\)左移的时候,两部分的最大和都会减少,因此我们只需要找出最右侧的\(p\)满足上述限制
这个显然具有可二分性,总复杂度\(O(n\log n)\)
#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,m,a[N<<1],pos,ans;
inline bool check(CI x)
{
for (RI i=(x<<1)+1,j=(n<<1);i<=j;++i,--j) if (a[i]+a[j]<m) return 0; return 1;
}
int main()
{
RI i,j; for (scanf("%d%d",&n,&m),i=1;i<=(n<<1);++i)
scanf("%d",&a[i]); sort(a+1,a+(n<<1)+1);
int l=0,r=n,mid; while (l<=r)
if (check(mid=l+r>>1)) pos=mid,r=mid-1; else l=mid+1;
for (i=1,j=(pos<<1);i<=j;++i,--j) ans=max(ans,a[i]+a[j]);
for (i=(pos<<1)+1,j=(n<<1);i<=j;++i,--j) ans=max(ans,a[i]+a[j]-m);
return printf("%d",ans),0;
}
Postscript
暑假转瞬即逝,做题遥遥无期