Codeforces Round #615 (Div. 3)补题

2020年1月24日,武汉加油,全国医疗工作者加油。
可能是我这三个月以来打的最菜的一场了……
Codeforces Round #615 (Div. 3)

本场关键词

读题、贪心、数学、乱搞、树的直径

A. Collecting Coins

给你 n n n a , b , c a,b,c a,b,c,问你是否可以将 n n n完全分配给 a , b , c a,b,c a,b,c,使分配后三者数量相同。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=0x3f3f3f3f;
int main()
{
	ll n,t,a[3];
	cin>>t;
	while(t--)
	{
		for(int i=0;i<3;i++)
			cin>>a[i];
		cin>>n;
		sort(a,a+3);
		int dis=a[2]-a[1]+a[2]-a[0];
		if(n>=dis&&(n-dis)%3==0)
			cout<<"YES\n";
		else
			cout<<"NO\n";
	}
	return 0;
}

B. Collecting Packages

题意

在网格上有n个宝箱,第 a i a_i ai个坐标为 ( x i , y i ) (x_i,y_i) (xi,yi),机器人初始坐标为 ( 0 , 0 ) (0,0) (0,0),只能向右或向上移动,问是否可以拿到全部宝箱,若可以,输出移动路线(优先选择向右移动)。

思路

x x x轴排序,然后贪心

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1005,inf=0x3f3f3f3f;
struct node
{
	int x,y;
} a[maxn];
bool cmp(const node&a,const node&b)
{
	return a.x<b.x;
}
bool cmp2(const node&a,const node&b)
{
	return a.y<b.y;
}
int main()
{
	ll n,t;
	cin>>t;
	while(t--)
	{
		cin>>n;
		for(int i=1;i<=n;i++)
			cin>>a[i].x>>a[i].y;
		sort(a+1,a+n+1,cmp2);
		stable_sort(a+1,a+n+1,cmp);
		bool flag=1;
		int nx=0,ny=0;
		string ans;
		for(int i=1;i<=n;i++)
		{
			if(a[i].y<ny||a[i].x<nx)
			{
				flag=0;
				break;
			}
			while(a[i].x>nx)
			{
				nx++;
				ans+='R';
			}
			while(a[i].y>ny)
			{
				ny++;
				ans+='U';
			}
		}
		if(flag)
		{
			cout<<"YES\n"<<ans<<endl;
		}
		else
			cout<<"NO\n";
	}
	return 0;
}

C. Product of Three Numbers

woc这题我演了两个小时,最后发现是没开根号的锅。

题意

给你一个整数 n n n,找到三个大于等于2不同整数 a , b , c a,b,c a,b,c,使得 a b c = n abc=n abc=n,输出 a , b , c a,b,c a,b,c

思路

先打1e5内质数表,在质数表内查询 n n n的第一个因数 a a a,如果没找到,则 n n n无法分解。
然后在 ( a , n   ] (a,\sqrt{n}\ ] (a,n  ]枚举,找到第二个因数 b b b,此时 n a b \frac{n}{ab} abn即为第三个因数 c c c,判断 c c c是否大于 b b b,若不大于 b b b,则无解,否则输出答案。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1005,inf=0x3f3f3f3f;
const int MAXN=1e5+3;
ll prime[MAXN+1],primesize;//primesize是素数的个数,prime[i]存着第i个素数
bool isprime[MAXN+1];
void getlist(void)
{
	memset(isprime,1,sizeof(isprime));
	primesize=0;
	isprime[1]=false;
	for(int i=2;i<=MAXN;i++)
	{
		if(isprime[i])
			prime[++primesize]=i;
		for(int j=1;j<=primesize&&i*prime[j]<=MAXN;j++)
		{
			isprime[i*prime[j]]=false;
			if(i%prime[j]==0)
				break;
		}
	}
}
int main()
{
	ll t,n;
	getlist();
	scanf("%I64d",&t);
	while(t--)
	{
		scanf("%I64d",&n);
		bool flag=0;
		vector<ll>ans;
		int cnt=0;
		for(ll i=1;i<=primesize;i++)
		{
			if(n%prime[i]==0)
			{
				n/=prime[i];
				cnt++;
				ans.push_back(prime[i]);
				break;
			}
		}
		if(!cnt)
			printf("NO\n");
		else{
			ll lim=sqrt(n)+1;//开根号!!!
			for(ll i=ans[0]+1;i<=lim;i++)
			{
				if(n%i==0)
				{
					ans.push_back(i);
					n/=i;
					if(n>ans[1])
					{
						ans.push_back(n);
						flag=1;
						break;
					}
					else
						break;
				}
			}
			if(flag)
			{
				printf("YES\n");
				for(auto &i:ans)
					printf("%I64d ",i);
				printf("\n");
			}
			else
				printf("NO\n");
		}
	}
	return 0;
}

D. MEX maximizing

又是读不懂题的一天。
赛中以为只能在询问时挑选集合中一个元素并将其加减 x x x一次,然后又开始演起来了……

题意

M E X MEX MEX运算为不在集合中的最小非负整数。
一开始,你有一个空集合 a a a和一个整数 x x x q q q次询问,第 j j j次询问给出一个整数 y j y_j yj,并将 y j y_j yj加入集合中。
您可以选择集合中的任意元素 a i a_i ai,并将其加减 x x x任意次(只要保证不为负)。
对于第 j j j个询问,输出加入 y j y_j yj后集合 a a a的最大 M E X MEX MEX

思路

数论,贪心。
因为可以加减 x x x的任意倍,所以集合中的元素只与 x x x的余数有关。
a n s ans ans即为当前集合 a a a M E X MEX MEX,可以发现 a n s ans ans一定不会变小,初始为0。
存储的时候只存储对应余数的个数,所以当余数为 a n s % x ans\%x ans%x的数量不为0时,可以将一个 a n s % x ans\%x ans%x通过增加特定个 x x x来得到元素 a n s ans ans,此时 M E X MEX MEX在原来的基础上 + 1 +1 +1,当 a n s ≤ x ans\le x ansx时也是如此。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=4e5+5;
int arr[maxn];
int main()
{
	int q,x,y,ans=0;
	cin>>q>>x;
	while(q--)
	{
		cin>>y;//x可以增减任意次
		arr[y%x]++;//那么只和余数有关
		while(arr[ans%x]>0)//
		{
			arr[ans%x]--;//拿出一个ans%x变为ans
			ans++;
		}
		cout<<ans<<endl;
	}
	return 0;
}

E. Obtain a Permutation

思路很清晰……就是太麻烦……Orz,还是码力不够……

题意

给你一个 n × m n\times m n×m矩阵,每次你可以选择一下一个操作:

  • 选择任何一个元素,并将其变为 [ 1 , n m ] [1,nm] [1,nm]范围内任何一个数。
  • 将某一列第一行元素放到最后一行。

求变为要求形式矩阵最小操作次数。

思路

静态内存开不下……要开vector存数组。
很明显列和列之间不会相互影响,所以每一列单独计算最优方案即可。
对于第 i i i列, O ( n ) O(n) O(n)枚举符合该列要求的元素到符合位置的步长,并且用 c n t [ j ] cnt[j] cnt[j]记录步长为 j j j i i i列元素个数。该列的贡献即为移动的代价+修改的代价最小值,详见代码……

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5,inf=0x3f3f3f3f;
vector<int> a[maxn];
int cnt[maxn];
int main()
{
	int n,m,tmp,ans=0;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		a[i].push_back(0);
		for(int j=1;j<=m;j++)
		{
			cin>>tmp;
			a[i].push_back(tmp);
		}
	}
	for(int i=1;i<=m;i++)//每一列单独计算
	{
		int now=inf;
		for(int j=0;j<=n;j++)
			cnt[j]=0;//向上移动i位符合的元素
		for(int j=1;j<=n;j++)//第j行元素向上移动x行到合适位置
		{
			if(a[j][i]<1||a[j][i]>n*m||(a[j][i]-i)%m!=0)//不符合这一列的元素
				continue;
			int sup=(a[j][i]-i)/m+1;//应该在的行数
			cnt[(j-sup+n)%n]++;
		}
		for(int j=0;j<n;j++)
			now=min(now,j+n-cnt[j]);
		ans+=now;
	}
	cout<<ans<<endl;
	return 0;
}

附:TLE代码

很明显的思路……复杂度 O ( n 3 ) O(n^3) O(n3),妥妥的TLE

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5,inf=0x3f3f3f3f;
vector<int> a[maxn];
int n,m;
int jud(int col,int k)//第col列向上窜k个的代价
{//a[i][j]应为(i-1)*m+j
	int ret=0;
	for(int i=1;i<=n;i++)//枚举第i行
	{
		int now=(i-k+n)%n;//窜之后的所在行
		if(now==0)
			now=n;
		if(a[i][col]!=(now-1)*m+col)
			ret++;
	}
	return ret;
}
int main()
{
	int tmp,ans=0;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		a[i].push_back(0);
		for(int j=1;j<=m;j++)
		{
			cin>>tmp;
			a[i].push_back(tmp);
		}
	}
	for(int i=1;i<=m;i++)//每一列单独计算
	{
		int now=inf;
		for(int j=0;j<n;j++)
			now=min(now,jud(i,j)+j);
		ans+=now;
	}
	cout<<ans<<endl;
	return 0;
}

F. Three Paths on a Tree

Orz,读题又双叒叕失误,这种图论题我也能演起来……

题意

在树上寻找三个节点 a , b , c a,b,c a,b,c,使得 a , b , c a,b,c a,b,c三个节点间的路径最长。

思路

树上距离+树的直径。
很显然三条路径 ( a , b ) , ( b , c ) , ( a , c ) (a,b),(b,c),(a,c) (a,b),(b,c),(a,c)有一条为树的直径(我说显然是因为我不会证明呜呜呜)。
先任选一个点,进行BFS,找到树的直径的一端,记录为 a a a,再以 a a a为源点进行BFS,求出树的直径另一端点 b b b,以及树上所有点到 a a a的距离,记录到 r e c 1 [ m a x n ] rec1[maxn] rec1[maxn],再以 b b b为源点进行BFS,求出树上所有点到 b b b的距离记录到 r e c 2 [ m a x n ] rec2[maxn] rec2[maxn]
此时我们知道了书上任意一节点到 a a a b b b的距离,可以枚举得到 c c c,注意 c c c不能为 a a a b b b即可。
a n s = d i s t ( a , b ) + d i s t ( a , c ) + d i s t ( b , c ) 2 ans=\frac{dist(a,b)+dist(a,c)+dist(b,c)}{2} ans=2dist(a,b)+dist(a,c)+dist(b,c)

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
vector<int>G[maxn];
int root=1,dist[maxn],rec1[maxn],rec2[maxn];
bool vis[maxn];
void bfs(int rt)
{
	memset(vis,0,sizeof(vis));
	queue<int>QAQ;
	QAQ.push(rt);
	dist[rt]=0;
	vis[rt]=1;
	while(!QAQ.empty())
	{
		int u=QAQ.front();
		if(dist[u]>dist[root])
			root=u;
		QAQ.pop();
		for(auto &v:G[u])
		{
			if(vis[v])
				continue;
			vis[v]=1;
			dist[v]=dist[u]+1;
			QAQ.push(v);
		}
	}
}
int main()
{
	int n,u,v;
	cin>>n;
	for(int i=1;i<n;i++)
	{
		cin>>u>>v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	bfs(1);//找到一个端点a
	int a=root,b,c=0,ans;
	bfs(root);//求出直径,找到另一个端点b
	b=root,ans=dist[root];
	memcpy(rec1,dist,sizeof(int)*(n+1));//节点到a距离
	bfs(root);//求出节点到b距离
	memcpy(rec2,dist,sizeof(int)*(n+1));
	for(int i=1;i<=n;i++)
	{
		if(i!=a&&i!=b&&rec1[i]+rec2[i]>rec1[c]+rec2[c])
			c=i;
	}
	ans=(ans+rec1[c]+rec2[c])/2;
	cout<<ans<<endl;
	cout<<a<<' '<<b<<' '<<c<<endl;
	return 0;
}

2020年1月24日17点27分

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值