Atcoder Grand Contest 032

打的第一场Atcoder,已经知道会被虐得很惨,但没有想到竟然只做出一题……

思维急需提升。

A - Limited Insertion

这题还是很签到的。

感觉正着做不好做,我们反着来,把加数变为删数。

显然每次有多个可以删时要删最后一个可删的,这样前面仍然合法,后面也有可能有更多合法情况。

发现不能删时就puts("-1")

#include<bits/stdc++.h>
namespace my_std{
	using namespace std;
	#define rep(i,x,y) for (int i=(x);i<=(y);i++)
	#define drep(i,x,y) for (int i=(x);i>=(y);i--)
	#define sz 233
}
using namespace my_std;

int n;
vector<int>v;
int ans[sz];

int main()
{
	cin>>n;
	int x;
	rep(i,1,n) cin>>x,v.push_back(x);
	drep(i,n,1)
	{
		bool flg=0;
		drep(j,i-1,0) if (v[j]==j+1) {flg=1;ans[i]=j+1;v.erase(v.begin()+j);break;}
		if (!flg) return puts("-1"),0;
	}
	rep(i,1,n) printf("%d\n",ans[i]);
	return 0;
}

B - Balanced Neighbours

首先手玩出n=3,4,5的情况。

观察一下图,发现\(n=3\)\((1,2)\)未连边,\(n=4\)\((1,4),(2,3)\)未连边,\(n=5\)\((1,4),(2,3)\)未连边。

\(1+2=3,1+4=2+3=5\)

猜一下结论,发现很容易证明,就做完了。

#include<bits/stdc++.h>
namespace my_std{
	using namespace std;
	#define pii pair<int,int>
	#define fir first
	#define sec second
	#define MP make_pair
	#define rep(i,x,y) for (int i=(x);i<=(y);i++)
}
using namespace my_std;

int main()
{
	int n;
	cin>>n;
	vector<pii>v;
	rep(i,1,n) rep(j,i+1,n) if (i+j!=n+(!(n&1))) v.push_back(MP(i,j));
	printf("%d\n",(int)v.size());
	rep(i,0,(int)v.size()-1) printf("%d %d\n",v[i].fir,v[i].sec);
	return 0;
}

C - Three Circuits

这题感觉好神仙啊……怎么可能做得出啊……我菜死了啊……

首先原图显然要存在一条欧拉回路,也就是度数全都为偶数。

如果有一个点的度数大于等于6,那么以它为起点,可以至少拆出3个欧拉回路,满足条件。

除去上面的情况,如果有大于等于3个点的度数为4,那么画画图会发现也是合法的。

除去上面的情况,如果有2个点度数为4而且它们之间恰好有2条路径,那么也是合法的。

除去以上情况,全都不合法。

然而考试时怎么敢那么肯定啊!!

#include<bits/stdc++.h>
namespace my_std{
	using namespace std;
	#define pii pair<int,int>
	#define fir first
	#define sec second
	#define MP make_pair
	#define rep(i,x,y) for (int i=(x);i<=(y);i++)
	#define drep(i,x,y) for (int i=(x);i>=(y);i--)
	#define go(x) for (int i=head[x];i;i=edge[i].nxt)
	#define templ template<typename T>
	#define sz 101010
	typedef long long ll;
	typedef double db;
	templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
	templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
	templ inline void read(T& t)
	{
		t=0;char f=0,ch=getchar();double d=0.1;
		while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
		while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
		if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
		t=(f?-t:t);
	}
	template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
}
using namespace my_std;

int n,m;
struct hh{int t,nxt;}edge[sz<<1];
bool vis[sz];
int head[sz],ecnt;
int deg[sz];
void make_edge(int f,int t)
{
	edge[++ecnt]=(hh){t,head[f]};
	head[f]=ecnt;
	edge[++ecnt]=(hh){f,head[t]};
	head[t]=ecnt;
	++deg[f],++deg[t];
}

void out(string s){cout<<s;exit(0);}

int p1,p2;
int e;
int dfs(int x,bool t)
{
	if (x==p1&&!t) return 1;
	if (x==p2) return ++e,2; 
	#define v edge[i].t
	go(x) if (!vis[(i+1)>>1])
	{
		vis[(i+1)>>1]=1;
		int r=dfs(v,0);
		if (r==1&&x!=p1) return 1;
		if (r==2&&x!=p1) return 2;
	}
	#undef v
	return 0;
}

int main()
{
	read(n,m);
	int x,y;
	rep(i,1,m) read(x,y),make_edge(x,y);
	rep(i,1,n) if (deg[i]&1) out("No");
	rep(i,1,n) if (deg[i]>=6) out("Yes");
	int cnt=0;vector<int>v;
	rep(i,1,n) if (deg[i]==4) ++cnt,v.push_back(i);
	if (cnt>=3) out("Yes");
	if (cnt<=1) out("No");
	p1=v[0],p2=v[1];
	dfs(p1,1);
	out((e==2)?"Yes":"No"); 
	return 0;
}

D - Rotation Sort

容易想到把旋转看作把一个数往左边/右边插入。

但插入后其他数的位置会改变,不好处理。

考虑把位置由整数域扩展为实数域,这样其他数就可以不用改变位置了。

然后就可以DP了。设\(dp_{i,j}\)表示前\(i\)个数在数轴上单调递增,第\(i\)个数处于\([j,j+1)\)内,所需的最小代价,转移方程显然。

用前缀最小值优化一下即可。

#include<bits/stdc++.h>
namespace my_std{
	using namespace std;
	#define rep(i,x,y) for (int i=(x);i<=(y);i++)
	#define drep(i,x,y) for (int i=(x);i>=(y);i--)
	#define templ template<typename T>
	#define sz 5050
	typedef long long ll;
	templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
	templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
	templ inline void read(T& t)
	{
		t=0;char f=0,ch=getchar();double d=0.1;
		while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
		while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
		if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
		t=(f?-t:t);
	}
	template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
}
using namespace my_std;

int n;
ll A,B;
int pos[sz];

ll dp[sz][sz],mn[sz][sz];

int main()
{
	read(n,A,B);
	int x;
	rep(i,1,n) read(x),pos[x]=i;
	memset(dp,0x3f,sizeof(dp));memset(mn,0x3f,sizeof(mn));
	rep(i,0,n) dp[0][i]=mn[0][i]=0;
	rep(i,1,n) rep(j,0,n)
	{
		if (pos[i]==j) dp[i][j]=min(mn[i-1][j-1],dp[i-1][j]+A);
		else if (pos[i]>j) dp[i][j]=mn[i-1][j]+B;
		else dp[i][j]=mn[i-1][j]+A;
		mn[i][j]=min(dp[i][j],j?mn[i][j-1]:ll(1e15));
	}
	cout<<mn[n][n];
	return 0;
}

E - Modulo Pairing

使劲找规律,发现最优解一定是中间一个分界线,左边1k,2k-1,...全都不超过m,右边同样k+1n+n,k+2n+n-1,...,全都超过m。

这个规律也不是很难证明(吧,也许我思维不够严谨)。

然后还发现分界线越往左,左边就更容易合法,右边更容易不合法,(在合法情况下)答案更小。

然后就可以二分最左的分界点了。

#include<bits/stdc++.h>
namespace my_std{
	using namespace std;
	#define rep(i,x,y) for (int i=(x);i<=(y);i++)
	#define sz 101010 
}
using namespace my_std;

int n,m;
int a[sz<<1];

bool check(int p)
{
	rep(i,p+1,n+n) if (a[i]+a[n+n-i+p+1]<m) return 0;
	return 1;
}
int calc(int p)
{
	int ret=0;
	rep(i,1,p) ret=max(ret,a[i]+a[p-i+1]);
	rep(i,p+1,n+n) ret=max(ret,a[i]+a[n+n-i+p+1]-m);
	return ret;
}

int main()
{
	cin>>n>>m;
	rep(i,1,n*2) scanf("%d",&a[i]);
	sort(a+1,a+n+n+1);
	int l=0,r=n,pos;
	while (l<=r)
	{
		int mid=(l+r)>>1;
		if (check(mid*2)) pos=mid,r=mid-1;
		else l=mid+1;
	}
	cout<<calc(pos*2);
	return 0;
}

E看起来好难,弃了qwq。

posted @ 2019-03-23 23:39  p_b_p_b  阅读(309)  评论(0编辑  收藏  举报