AtCoder Grand Contest 032
链接
C. Three Circuits
首先显然需要有欧拉回路,即所有点度数都是偶数。
假如有 \(6\) 度的点或者超过两个 \(4\) 度点一定合法。如果不到两个 \(4\) 度点就不合法。
当有两个 \(4\) 度点时,考虑是否连成两个环,直接删去一个 \(4\) 度点后判是否连通即可。
复杂度 \(O(n)\)。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200010
using namespace std;
int f[N];
int find(int x){return f[x]==x?f[x]:(f[x]=find(f[x]));}
int x[N],y[N],d[N];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]),d[x[i]]++,d[y[i]]++;
for(int i=1;i<=n;i++) if(d[i]&1){puts("No");return 0;}
for(int i=1;i<=n;i++) if(d[i]>4){puts("Yes");return 0;}
int c=0;
for(int i=1;i<=n;i++) if(d[i]==4) c++;
if(c>2){puts("Yes");return 0;}
if(c<2){puts("No");return 0;}
for(int i=1;i<=n;i++) if(d[i]==4){c=i;break;}
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++) if(x[i]!=c && y[i]!=c) f[find(x[i])]=find(y[i]);
int d=find(c==1?2:1);
for(int i=1;i<=n;i++) if(i!=c && find(i)!=d){puts("Yes");return 0;}
puts("No");
return 0;
}
D. Rotation Sort
可以发现 \(1\) 操作本质上是将一个数往右插,\(2\) 就是把一个数往左插。
从左往右处理,考虑一个数如果向右插,等同于将其扔掉(直接计入答案)。所以考虑维护一个上升的序列表示没有向右插的集合。显然只要记录这个序列的最大值即可。
用 \(f_{i,j}\) 表示前 \(i\) 个数,上升序列最大值为 \(j\) 的最小代价。
考虑新加数为 \(x\),如果 \(x>j\) 那么可以向右插或者加入序列。如果 \(x<j\) 就只能向左插入序列。
复杂度 \(O(n^2)\)。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 5010
#define ll long long
using namespace std;
ll f[N][N];
int p[N];
void chkmin(ll &x,ll y){x=min(x,y);}
int main()
{
int n,a,b;
scanf("%d%d%d",&n,&a,&b);
for(int i=1;i<=n;i++) scanf("%d",&p[i]);
memset(f,0x3f,sizeof(f));
f[0][0]=0;
for(int i=1;i<=n;i++)
for(int j=0;j<=n;j++)
{
if(p[i]<j) chkmin(f[i][j],f[i-1][j]+b);
else chkmin(f[i][j],f[i-1][j]+a),chkmin(f[i][p[i]],f[i-1][j]);
}
ll ans=1e16;
for(int i=1;i<=n;i++) ans=min(ans,f[n][i]);
printf("%lld\n",ans);
return 0;
}
E. Modulo Pairing
显然只有两种可能:相加不超过 \(M\) 与超过 \(M\)。
考虑先将原序列排序,可以证明,一定存在一个分界线,满足分界线左边右边各自首尾匹配。具体证明分类讨论一下即可。
右边部分显然要所有匹配 \(>m\),而右边和显然是单减的,所以可以二分。
总复杂度 \(O(n\log n)\)。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200010
#define ll long long
using namespace std;
int a[N],n,m,ans=2e9;
bool check(int x)
{
for(int i=1;2*x+2*i<=2*n;i++) if(a[2*x+i]+a[2*n-i+1]<m) return false;
return true;
}
void upd(int x)
{
int res=0;
for(int i=1;i<=x;i++) res=max(res,(a[i]+a[2*x-i+1])%m);
for(int i=1;2*x+2*i<=2*n;i++) res=max(res,(a[2*x+i]+a[2*n-i+1])%m);
ans=min(ans,res);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=2*n;i++) scanf("%d",&a[i]);
sort(a+1,a+2*n+1);
int l=0,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) upd(mid),r=mid-1;
else l=mid+1;
}
printf("%d\n",ans);
return 0;
}
F. One Third
考虑距离 \(\frac 13\) 最近很难做,如果将序列每 \(\frac 13\) 分成一块并且映射到一起,可以发现任意两个不同色的点之间距离就是到 \(\frac 13\) 的距离。
这样转化为 \(n\) 个点之间的不同色点距离最小值。
考虑一个式子:
其中 \(L_{k}\) 表示第 \(k\) 小的距离。具体证明可以用 min-max 容斥然后随便推一推。
这样枚举 \(n\) 个点之间第 \(k\) 对相邻点是第一对不同色点,那么前 \(k\) 对一定是同色点,有:
直接处理即可,复杂度 \(O(n)\)。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1000010
#define mod 1000000007
using namespace std;
int ksm(int a,int b=mod-2)
{
int r=1;
for(;b;b>>=1)
{
if(b&1) r=1ll*r*a%mod;
a=1ll*a*a%mod;
}
return r;
}
int _3[N];
int main()
{
int n;
scanf("%d",&n);
_3[1]=ksm(3),_3[0]=1;
for(int i=2;i<n;i++) _3[i]=1ll*_3[i-1]*_3[1]%mod;
int ans=0,res=ksm(n);
for(int i=1;i<=n;i++)
{
ans=(ans+1ll*(_3[i-1]-_3[i])%mod*res)%mod;
res=(res+ksm(n-i))%mod;
}
printf("%lld\n",1ll*ans*ksm(3*n)%mod);
return 0;
}

浙公网安备 33010602011771号