AtCoder Grand Contest 032

  A:倒序考虑,每次删除最后一个合法数即可,正确性显然。好久没有atcoder比赛我又忘了评测机并没有define online_judge白交了一发。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 110 
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n,a[N],ans[N];
signed main()
{
	n=read();
	for (int i=1;i<=n;i++) a[i]=read();
	for (int i=n;i>=1;i--)
		for (int j=i;j>=1;j--)
		if (a[j]==j)
		{
			for (int k=j;k<i;k++) a[k]=a[k+1];
			ans[i]=j;
			break;
		}
	for (int i=1;i<=n;i++) if (!ans[i]) {cout<<-1;return 0;}
	for (int i=1;i<=n;i++) cout<<ans[i]<<endl;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  B:容易发现当n%4==0时,可以将图划分成一个二分图,使其两边和相等,然后中间所有边都连上即可。类似的可以发现,只要将图划分成若干和相同的集合即可。那么根据n的奇偶性讨论一下就好了,偶数时小的和大的两两匹配,奇数时把n单独列为一个集合同样两两匹配。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define N 110 
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n;
vector<int> a[N];
signed main()
{
	n=read();
	int s=n*(n+1)/2;
	if (n%2==0)	for (int i=1;i<=n/2;i++) a[i].push_back(i),a[i].push_back(n-i+1);
	if (n%2==1)
	{
		for (int i=1;i<=n/2;i++) a[i].push_back(i),a[i].push_back(n-i);
		a[(n+1)/2].push_back(n);
	}
	int ans=0;
	for (int i=1;i<=(n+1)/2;i++)
		for (int j=i+1;j<=(n+1)/2;j++)
		if (i!=j) ans+=a[i].size()*a[j].size();
	cout<<ans<<endl;
	for (int i=1;i<=(n+1)/2;i++)
		for (int j=i+1;j<=(n+1)/2;j++)
		if (i!=j) 
			for (int x=0;x<a[i].size();x++)
				for (int y=0;y<a[j].size();y++)
				cout<<a[i][x]<<' '<<a[j][y]<<endl;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  C:首先显然必须得存在欧拉回路。那么即要把跑出来的欧拉回路拆成三个回路。显然在欧拉回路所经过的点序列中,找到一个首尾点均相同的段抽出来,就是一个回路。那么检查序列中是否有点出现三次或以上,若有显然有解;否则检查出现两次的点,这样的点需要有两个或以上,如果其中两个点能构成不相交的段则有解。注意到若有至少三个点一定有解,稍微讨论一下即可证明。那么只需要考虑有两个点的情况,判一下是否相交。实际上并不需要跑欧拉回路,通过度数讨论一下即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 100010
#define id(i) (((i)+1)/2)
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n,m,p[N],degree[N],cnt[N],t,stk[N<<1],top,tot;
struct data{int to,nxt;
}edge[N<<1];
bool flag[N];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void dfs(int k,int t)
{
	if (k==t) {tot++;return;}
	flag[k]=1;
	for (int i=p[k];i;i=edge[i].nxt)
	if (!flag[edge[i].to]||edge[i].to==t)
	{
		dfs(edge[i].to,t);
	}
}
signed main()
{
	n=read(),m=read();
	for (int i=1;i<=m;i++)
	{
		int x=read(),y=read();
		degree[x]++,degree[y]++;
		addedge(x,y),addedge(y,x);
	}
	for (int i=1;i<=n;i++) if (degree[i]&1) {cout<<"No";return 0;}
	for (int i=1;i<=n;i++) if (degree[i]>=6) {cout<<"Yes";return 0;}
	int cnt=0;
	for (int i=1;i<=n;i++) if (degree[i]>=4) {cnt++;}
	if (cnt>=3) {cout<<"Yes";return 0;}
	if (cnt<=1) {cout<<"No";return 0;}
	int x=0,y=0;for (int i=1;i<=n;i++) if (degree[i]>=4) if (x) y=i;else x=i;
	dfs(x,y);
	if (tot==2) cout<<"Yes";else cout<<"No";
	return 0;
	//NOTICE LONG LONG!!!!!
}

  D:将移动序列看成移动一个数,进一步看成将一个数放到一个实数位置上,代价根据是向左还是向右还是不动不同。这样dp就很显然了,即将所有数从小到大排序(相同的数按初始位置排序)后,设f[i][j]为第i个数放在第j段时,前i个数的最小代价,转移显然。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 5010
#define inf 1000000000000000ll
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n,A,B;
struct data
{
	int x,y;
	bool operator <(const data&a) const
	{
		return x<a.x||x==a.x&&y<a.y;
	}
}a[N];
ll f[N][N<<1];
signed main()
{
	n=read(),A=read(),B=read();
	for (int i=1;i<=n;i++) a[i].x=read(),a[i].y=i;
	sort(a+1,a+n+1);
	for (int i=1;i<=n;i++)
	{
		ll s=inf;
		for (int j=0;j<=n*2;j++)
		{
			s=min(s,f[i-1][j]);
			if (j&1)
			{
				f[i][j]=s+((j+1>>1)==a[i].y?0:((j+1>>1)<a[i].y?B:A));
			}
			else
			{
				f[i][j]=s+(((j>>1)<a[i].y?B:A));
			}
		}
	}
	ll ans=inf;
	for (int i=0;i<=n*2;i++) ans=min(ans,f[n][i]);
	cout<<ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  E:如果不考虑%m的话显然小的和大的配对。可以证明若将需要减m的对和不需要减m的对分成两组,一定存在最优方案使得不需要减m的是一段前缀而需要减m的是一段后缀,并且其分别满足上述贪心策略。具体讨论几种情况可以发现交换不会更劣。显然前后缀的分界点确定则方案确定,注意到分界点移动时两边答案单调移动且方向相同,那么二分找到最靠前的合法分界点(即满足上述条件)即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 200010 
#define inf 2000000001
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
	int x=0,f=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x*f;
}
int n,m,a[N],ans;
int calc_head(int k)
{
	int s=0;
	for (int i=1;i<=k/2;i++) 
	{
		if (a[i]+a[k-i+1]>=m) return inf;
		s=max(s,a[i]+a[k-i+1]);
	}
	return s;
}
int calc_tail(int k)
{
	int s=0;
	for (int i=k;i<=n;i++)
	{
		if (a[i]+a[n-(i-k)]<m) return inf;
		s=max(s,a[i]+a[n-(i-k)]-m);
	}
	return s;
}
signed main()
{
	n=read()*2,m=read();
	for (int i=1;i<=n;i++) a[i]=read();
	sort(a+1,a+n+1);
	int x;
	int L=0,R=n/2;
	while (L<=R)
	{
		int mid=L+R>>1;
		if (calc_tail(mid*2+1)==inf) L=mid+1;
		else R=mid-1,x=mid*2;
	}
	cout<<max(calc_head(x),calc_tail(x+1));
	return 0;
	//NOTICE LONG LONG!!!!!
}

  F:咕

  result:rank 177 rating +46

原文地址:https://www.cnblogs.com/Gloid/p/10604980.html