Codeforces Round #534 Div. 1

  A:用一列放竖着的方块,两列放横着的方块。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 1010
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;
char s[N];
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
#endif
	scanf("%s",s+1);n=strlen(s+1);
	int x=0,y=0;
	for (int i=1;i<=n;i++)
	if (s[i]=='0')
	{
		x++;
		if (x&1) cout<<1<<' '<<1<<endl;
		else cout<<3<<' '<<1<<endl;
	}
	else
	{
		y++;if (y>4) y=1;
		cout<<y<<' '<<2<<endl;
	}
	return 0;
	//NOTICE LONG LONG!!!!!
}

  B:先询问2k 2k+1,假设已经知道了a>2k,则当a>2k+1时,后者大,否则前者大。这样可以倍增出一个a的取值区间。注意到在区间内x mod a值开始单增,中间突变为0(即x=a),然后又单增,并且最开始的值要比最终的值大。于是拿2k+1和一个二分值比较即可。注意特判a=1的情况,询问0和1即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
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;
}
char s[10];
signed main()
{
	cin>>(s+1);
	while (s[1]!='e')
	{
		cout<<'?'<<' '<<0<<' '<<1<<endl;
		char c;cin>>c;
		if (c=='x') cout<<'!'<<' '<<1<<endl;
		else
		{
			int x;
			for (int i=0;i<30;i++)
			{
				cout<<'?'<<' '<<(1<<i)<<' '<<(1<<i+1)<<endl;
				cin>>c;
				if (c=='x') {x=i;break;}
			}
			int y=(1<<x+1);
			int l=(1<<x)+1,r=y-1,ans=y;
			while (l<=r)
			{
				int mid=l+r>>1;
				cout<<'?'<<' '<<mid<<' '<<y<<endl;
				cin>>c;
				if (c=='x') l=mid+1;
				else ans=mid,r=mid-1;
			}
			cout<<'!'<<' '<<ans<<endl;
		}
		cin>>(s+1);
	}
	return 0;
	//NOTICE LONG LONG!!!!!
}

 

  C:二选一当然是考虑问题间有什么关系。随便找一棵dfs树,如果深度>=n/k,那么显然有一条长度为n/k的简单路径;否则由抽屉原理,至少有一层的点数超过k,由此可得树的叶子数量超过k。注意到由于每个点度数至少为3,叶子至少会往上连两条边。我们考虑对每个叶子构造一个以其为标记的环。唯一的要求是环长不能为3的倍数,由于我们有至少两条边,所以可以得到至少三个包含该叶子的环,容易发现其中一定存在一个满足要求的。并且由于环长不会超过树的深度,所以总输出量也是O(n)的。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<map>
#include<cassert>
using namespace std;
#define ll long long
#define N 500010
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,k,p[N],deep[N],fa[N],t,root=1;
bool flag[N],isleaf[N];
struct data{int to,nxt;
}edge[N<<1];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void dfs(int k) 
{
	flag[k]=1;int son=0;
	for (int i=p[k];i;i=edge[i].nxt)
	if (!flag[edge[i].to])
	{
		deep[edge[i].to]=deep[k]+1;
		fa[edge[i].to]=k;
		son++;
		dfs(edge[i].to);
	}
	if (son==0) isleaf[k]=1;
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
#endif
	n=read(),m=read(),k=read();
	for (int i=1;i<=m;i++)
	{
		int x=read(),y=read();
		addedge(x,y),addedge(y,x);
	}
	deep[root]=1;dfs(root);
	for (int i=1;i<=n;i++)
	if (deep[i]>=n/k+(n%k>0))
	{
		cout<<"PATH"<<endl;cout<<deep[i]<<endl;
		for (int x=i;x!=root;x=fa[x]) printf("%d ",x);
		cout<<root;
		return 0;
	}
	cout<<"CYCLES"<<endl;
	for (int i=1;i<=n;i++)
	if (isleaf[i])
	{
		bool f=0;
		for (int j=p[i];j;j=edge[j].nxt)
		if (edge[j].to!=fa[i]&&(deep[i]-deep[edge[j].to])%3!=2) 
		{
			printf("%d\n",deep[i]-deep[edge[j].to]+1);
			for (int x=i;x!=edge[j].to;x=fa[x]) printf("%d ",x);
			printf("%d\n",edge[j].to);
			f=1;break;
		}
		if (!f)
		{
			int u=0,v=0;
			for (int j=p[i];j;j=edge[j].nxt)
			if (edge[j].to!=fa[i])
				if (!u) u=edge[j].to;else if (!v) v=edge[j].to;
				else break;
			if (deep[u]<deep[v]) swap(u,v);
			printf("%d\n",deep[u]-deep[v]+2);
			printf("%d ",i);
			while (u!=v) printf("%d ",u),u=fa[u];
			printf("%d\n",v);
		}
		k--;if (k==0) break;
	}
	return 0;
	//NOTICE LONG LONG!!!!!
}

  D:先求出所有数的gcd,显然每次对一个数操作都应该去除gcd至少一种质因子,给每个数只保留这些质因子。由于值域1e12,不同的质因子数量最多为11个,设该数为m,那么所需要操作的数也不会超过m个。显然可以得到一个dp,即f[i][j][k]为前i个数操作j个质因子状态为k的最小Σe。复杂度O(n·m·3m)。

  注意到对于这m种质因子次数均相同的数,只需要保留代价最小的m个。于是先去一下重,猜想去重之后剩下数的数量就不会特别多。但乘上m·3m后还是跑不动。进一步发现对于均能去除某一质因子集合的数,也只需要保留代价最小的m个。这样继续暴力去重,剩下数的数量不超过m·2m,并且同时我们可以求出每个数可以用于去除哪些集合,总集合数量是m·2m的。回到原dp中可以发现复杂度就变成了O(m2·3m)。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define ll long long
#define N 1000010
#define M 11
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
ll gcd(ll n,ll m){return m==0?n:gcd(m,n%m);}
ll read()
{
	ll 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,cnt[M],t,LG2[1<<M];
ll m,k,p[M],pw[M][100],tot[1<<M],f[M][1<<M];
bool flag[N];
struct data
{
	int v;ll x;
	bool operator <(const data&a) const
	{
		return x<a.x;
	}
}a[N],b[N];
vector<int> s[N];
priority_queue<data> q[1<<M];
bool cmp(const data&a,const data&b)
{
	return a.v<b.v;
}
signed main()
{
#ifndef ONLINE_JUDGE
	freopen("d.in","r",stdin);
	freopen("d.out","w",stdout);
#endif
	n=read(),k=read();
	for (int i=1;i<=n;i++) m=gcd(m,a[i].x=read());
	for (int i=1;i<=n;i++) a[i].v=read();
	for (ll i=2;i*i<=m;i++)
	if (m%i==0)
	{
		p[t++]=i;
		while (m%i==0) m/=i;
	}
	if (m>1) p[t++]=m;if (t==0) {cout<<0;return 0;}
	for (int i=0;i<t;i++)
	{
		pw[i][0]=1;
		for (int j=1;j<100;j++) pw[i][j]=pw[i][j-1]*p[i];
	} 
	sort(a+1,a+n+1);
	int u=0;
	for (int i=1;i<=n;i++)
	{
		int v=i;
		while (a[v+1].x==a[i].x) v++;
		sort(a+i,a+v+1,cmp);
		for (int j=1;j<=min(t,v-i+1);j++)
		b[++u]=a[i+j-1];
		i=v;
	}
	swap(a,b);n=u;
	for (int i=0;i<M;i++) LG2[1<<i]=i;
	for (int j=1;j<(1<<t);j++)
		for (int i=0;i<t;i++)
		q[j].push((data){0,100000000000000ll});
	for (int i=1;i<=n;i++)
	{
		tot[0]=1;
		ll u=a[i].x;memset(cnt,0,sizeof(cnt));
		for (int j=0;j<t;j++)
		while (u%p[j]==0) cnt[j]++,u/=p[j];
		for (int j=1;j<(1<<t);j++)
		{
			tot[j]=tot[j^(j&-j)]*pw[LG2[j&-j]][cnt[LG2[j&-j]]];
			if (tot[j]<=k&&a[i].v<q[j].top().x) q[j].pop(),q[j].push((data){i,a[i].v});
		}
	}
	for (int j=1;j<(1<<t);j++)
	while (!q[j].empty())
	{
		data x=q[j].top();q[j].pop();
		s[x.v].push_back(j);
	}
	memset(f,42,sizeof(f));f[0][0]=0;
	for (int i=1;i<=n;i++)
		for (int j=t-1;j>=0;j--)
		{
			for (int k=0;k<s[i].size();k++)
			{
				int x=s[i][k];
				f[j+1][x]=min(f[j+1][x],f[j][0]+a[i].v);
				for (int y=(1<<t)-1^x;y;y=y-1&((1<<t)-1^x))
				f[j+1][x|y]=min(f[j+1][x|y],f[j][y]+a[i].v);
			}
		}
	ll ans=1000000000000000ll;
	for (int i=1;i<=t;i++)
	if (f[i][(1<<t)-1]<ans) ans=min(ans,i*f[i][(1<<t)-1]);
	if (ans>=1000000000000000ll) cout<<-1;
	else cout<<ans;
	return 0;
	//NOTICE LONG LONG!!!!!
}

  E感觉过于神仙。

posted @ 2019-02-21 02:27  Gloid  阅读(223)  评论(0编辑  收藏  举报