Codeforces Round #748 (Div. 3)

B. Make it Divisible by 25(思维)

题意:

给出一个数n,问最少去掉多少数位,能够使得所得到的新数为25的倍数?
(n≤1e18)

思路:

一个数为25的倍数,那么这样的数有一定的特点:
末尾为:25 或 50 或 75 或 x*100。

所以只需要找到2的位置和5的位置,将其中间的和5后面的所有位置去掉,就能保证所得到的新数是25的倍数了。50和75同理。
而所给的数 n 是没有前导0的,所以最后一种x*100的情况,只需要判断两个0就行了。找到两个0的位置,中间和第二个0后面的所有位置去掉。

数位长度很小,所以二重循环遍历找就行了。

Code:

const int N = 200010, mod = 1e9+7;
int T, n, m;
string a;

int main(){
    
    
	Ios;
	cin>>T;
	while(T--)
	{
    
    
		cin>>a;
		int n=a.size();
		a=" "+a;
		
		int ans=1e9;
		for(int i=1;i<=n;i++)
		{
    
    
			if(a[i]=='2')
			{
    
    
				for(int j=i+1;j<=n;j++)
				{
    
    
					if(a[j]=='5'){
    
    
						ans=min(ans,j-i-1+n-j);
					}
				}
			}
			if(a[i]=='5')
			{
    
    
				for(int j=i+1;j<=n;j++)
				{
    
    
					if(a[j]=='0'){
    
    
						ans=min(ans,j-i-1+n-j);
					}
				}
			}
			if(a[i]=='7')
			{
    
    
				for(int j=i+1;j<=n;j++)
				{
    
    
					if(a[j]=='5'){
    
    
						ans=min(ans,j-i-1+n-j);
					}
				}
			}
			if(a[i]=='0')
			{
    
    
				for(int j=i+1;j<=n;j++)
				{
    
    
					if(a[j]=='0'){
    
    
						ans=min(ans,j-i-1+n-j);
					}
				}
			}
		}
		cout<<ans<<endl;
	}
	
	return 0;
}

C. Save More Mice(贪心,模拟)

题意:

在一维坐标中,有一只猫在原点位置,有一个洞口在 k 位置(k≥2),原点和k位置之间,有n个老鼠。
现在每一秒,只能有一只老鼠移动一格,猫移动一格。猫会吃掉在所在位置上的老鼠,而老鼠移动到洞口 k 位置之后,便是安全的了。

问,最多能够有多少只老鼠是安全的?

思路:

因为所有老鼠所在位置在猫和洞口位置之间,而每次只能一只老鼠移动一格,为了使得安全老鼠尽量多,只能让距离洞口最近的那只老鼠先进洞。

按位置从后往前遍历所有老鼠:

  • 如果当前老鼠的位置大于前面老鼠浪费的时间,因为每次猫和老鼠轮流走一格,则说明这个老鼠是可以移动到洞口的。
    这个老鼠所浪费的时间为原来位置到洞口的位置之差。
  • 否则的话,当前及前面所有的老鼠都跑不掉。

Code:

const int N = 400010, mod = 1e9+7;
int T, n, m, a[N];

int main(){
    
    
	Ios;
	cin>>T;
	while(T--)
	{
    
    
		int k;
		cin>>k>>n;
		for(int i=1;i<=n;i++) cin>>a[i];
		sort(a+1,a+n+1);
		
		ll t=0,cnt=0;
		for(int i=n;i>=1;i--)
		{
    
    
			if(a[i]>t) t+=k-a[i],cnt++;
		}
		cout<<cnt<<endl;
	}
	
	return 0;
}

D1. All are Same(思维)

题意:

给出长度为 n 的数列,找出最大的 k,使得进行若干次下述操作,所有位置上的元素相同:选定一个位置 i i i,将此位置上的数 a[i] -= k ( n ≤ 40 , − 1 e 6 ≤ a [ i ] ≤ 1 e 6 ) (n≤40, -1e6≤a[i]≤1e6) (n40,1e6a[i]1e6)
如果 k 可以任意大,输出 -1。

思路:

因为改变一个位置只能减不能加,而把原来最小元素也 - k 的话,为了使所有元素相同,其他所有位置都要多减一个 k。

所以,最终所有位置上的值只能为原来所有元素中的最小值mina。

而满足条件的 k,必须让原来值减若干次之后,正好变成最小元素mina。
所以 k 要使所有位置的元素满足 (a[i]-mina)%k == 0

k 最大不超过所有数的最大绝对值*2,从大到小遍历所有的 k,看是否满足。

还需要考虑输出-1的情况,什么情况下 k 可以随意大呢?
所有数大小相同的时候,可以进行任意次操作,并且每次操作减任意值。

Code:

const int N = 200010, mod = 1e9+7;
int T, n, m, a[N];

int main(){
    
    
	Ios;
	cin>>T;
	while(T--)
	{
    
    
		cin>>n;
		int mina=1e9,maxa=-10,ff=0;
		for(int i=1;i<=n;i++){
    
    
			cin>>a[i];
			if(a[i]<mina) mina=a[i];
			if(abs(a[i])>maxa) maxa=abs(a[i]);
			
			if(i>1&&a[i]!=a[i-1]) ff=1;
		}
		
		if(!ff){
    
    
			cout<<-1<<endl;
			continue;
		}
		
		for(int i=2*maxa;i;i--)
		{
    
    
			bool flag=0;
			for(int j=1;j<=n;j++){
    
    
				if((a[j]-mina)%i!=0){
    
    
					flag=1;break;
				}
			}
			if(!flag){
    
    
				cout<<i<<endl;
				break;
			}
		}
	}
	
	return 0;
}

突然看到另一种做法,不需要暴力枚举所有 k:
求出所有数和 mina 之差,取所有差值的最大公约数为 k。

因为 k 是要被所有差值整除的,并且还要最大,那刚好是最大公约数!

这种做法时间复杂度就很优了。


E. Gardener and Tree(拓扑图)

题意:

对于一棵无根树,每次操作将这棵树的所有叶子节点删去。
问,m次操作后,最终剩余的节点个数?
在这里插入图片描述

思路:

对于分层操作,原来记的做法复杂度为O(n^2),现在这个做法复杂度为O(n+m)。

首先将入度为1的节点存入vector。
每次遍历vector中的所有节点,将与其相连的节点入度-1。如果删除入度之后,一节点的入度为1了,说明这个点是下一层需要删除的点,存入新一vector中,下一层用。

因为每个点只会遍历到一次,每条边最多用两次,所以复杂度O(N+M)。

所以之后遇到分级的题,用这种做法。

Code:

const int N = 800010;
int T, n, m, a[N];
int e[N],ne[N],h[N],idx;
int ru[N],f[N];

void add(int x,int y){
    
    
	e[idx]=y,ne[idx]=h[x],h[x]=idx++;
}

void tpsort()
{
    
    
	vector<int> v;
	for(int i=1;i<=n;i++) if(ru[i]==1) v.push_back(i);
	
	int cnt=0;
	while(v.size())
	{
    
    
		cnt++;
		vector<int> v1;
		for(int i=0;i<v.size();i++)
		{
    
    
			int x=v[i];
			f[x]=cnt;
			
			for(int j=h[x];j!=-1;j=ne[j])
			{
    
    
				int tx=e[j];
				ru[tx]--;
				if(ru[tx]==1) v1.push_back(tx);
			}
		}
		v=v1;
	}
}

int main(){
    
    
	Ios;
	cin>>T;
	while(T--)
	{
    
    
		cin>>n>>m;
		for(int i=1;i<=n;i++) ru[i]=0,f[i]=0,h[i]=-1;
		
		for(int i=1;i<n;i++)
		{
    
    
			int x,y;
			cin>>x>>y;
			add(x,y);
			add(y,x);
			ru[x]++,ru[y]++;
		}
		
		tpsort();
		
		int cnt=0;
		for(int i=1;i<=n;i++) if(f[i]>m) cnt++;
		cout<<cnt<<endl;
	} 
	
	return 0;
}

第二天更…
发现 这位大佬 还有另外一总做法:
记录每个点的层级数。对于新更新到点 t x tx tx 的层级,为更新这个点 x x x 的层级 + 1 +1 +1
直接跑拓扑排序就行了。

Code:

const int N = 800010, mod = 1e9+7;
int T, n, m, a[N];
int ru[N],f[N];
int e[N],ne[N],h[N],idx;

void add(int x,int y){
    
    
	e[idx]=y,ne[idx]=h[x],h[x]=idx++;
}

void tpsort()
{
    
    
	queue<int> que;
	for(int i=1;i<=n;i++){
    
    
		if(ru[i]==1) que.push(i),f[i]=1;
	}
	
	while(que.size())
	{
    
    
		int x=que.front();que.pop();
		
		for(int i=h[x];i!=-1;i=ne[i])
		{
    
    
			int tx=e[i];
			ru[tx]--;
			if(ru[tx]==1)
			{
    
    
				que.push(tx);
				f[tx]=f[x]+1;
			}
		}
	}
}

int main(){
    
    
	Ios;
	int T;
	cin>>T;
	while(T--)
	{
    
    
		cin>>n>>m;
		for(int i=1;i<=n;i++) h[i]=-1,f[i]=0,ru[i]=0;
		
		for(int i=1;i<n;i++)
		{
    
    
			int x,y;cin>>x>>y;
			add(x,y);add(y,x);
			ru[x]++,ru[y]++;
		}
		
		tpsort();
		
		int cnt=0;
		for(int i=1;i<=n;i++) if(f[i]>m) cnt++;
		cout<<cnt<<endl;
	}
	
	return 0;
}

哪里有问题或者不明白的地方欢迎留言评论~

猜你喜欢

转载自blog.csdn.net/Mr_dimple/article/details/120792477