Codeforces Round #705 (Div. 2) ABD

A: Anti-knapsack

Description

选出一个集合 { 1 , 2 , 3 , … … , n } \left\{ 1,2,3,……,n \right\} {1,2,3,,n}的子集,使得该子集中没有子集的元素和为 k k k,同时还要该使得该子集元素的个数最大化。

Solution

因为 1 < = k < = n 1<=k<=n 1<=k<=n,所以集合 { 1 , 2 , 3 , … … , n } \left\{ 1,2,3,……,n \right\} {1,2,3,,n}中的 { k + 1 , k + 2 , k + 3 , … … , n } \left\{ k+1,k+2,k+3,……,n \right\} {k+1,k+2,k+3,,n}是可以全部选出的,这些数的个数为 n − k n-k nk

t = ⌈ k 2 ⌉ t=\left \lceil \frac{k}{2} \right \rceil t=2k,然后根据手动模拟可以看出,下一步要取的数是 { t , t + 1 , t + 2 , t + 3 , … … , k − 1 } \left\{t,t+1,t+2,t+3,……,k-1 \right\} {t,t+1,t+2,t+3,,k1},这些数的个数为 ⌊ k 2 ⌋ \left \lfloor \frac k 2 \right \rfloor 2k

最后的总数 c n t = n − k + ⌊ k 2 ⌋ \quad cnt=n-k+\left \lfloor \frac k 2 \right \rfloor cnt=nk+2k

代码

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

bool vis[1005];

int main()
{
	int t;cin>>t;
	while(t--)
	{
		int n, k;
		cin>>n>>k;
	
		int cnt = n-k+k/2; //直接计数
		cout<<cnt<<endl;
        
		if(cnt<=0)
			continue;
        
		for(int i=n;i>=1;i--)
		{
			if(i>k)
				cout<<i<<" ";
			else if(i<k)
			{
				if(k%2==0 &&i>=k/2)
					cout<<i<<" ";
				if(k%2==1 &&i>k/2)
					cout<<i<<" ";
			}
						
		}
		cout<<endl;
	}
	return 0;
}

B:Planet Lapituletti

比较直接的模拟题,看懂题意后,直接模拟即可

注意 h h hh hh m m mm mm要进位,不过看到的镜像时间是啥就是啥,不要自己去进位(傻~~

成功条件很简单 i f ( n e w h < h a n d n e w m < m ) if(new_h<h\quad and\quad new_m<m) if(newh<handnewm<m)成立即可

注意前导0的输出,应该是可以一行给出的

printf("%02d:%02d",hh,mm);

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int tmp[12]={0,1,5,-1,-1,2,-1,-1,8,-1};
int f[120];

int main()
{
	for(int i=0;i<=9;i++)
	{
		for(int j=0;j<=9;j++)
		{ 
			int now = i*10+j;
			if(tmp[i]==-1||tmp[j]==-1)
			{
				f[now] = -1;		
				continue;
			}
			int rev = tmp[j]*10+tmp[i];
			f[now] = rev;
		}
	}

	int t; cin>>t;
	
	while(t--)
	{
		int h, m;
		cin>>h>>m;
		int hh,mm;
		scanf("%d:%d",&hh,&mm);	
		while(1)
		{
			if(mm==m)
				mm=0,hh++;
			if(hh==h)
				hh=0,mm=0;
			if(f[hh]==-1 || f[mm]==-1){
				mm++;continue;
			}
		
			
			int n_hh = f[mm], n_mm = f[hh];
			if(n_hh>=h || n_mm>=m){
				mm++;continue;
			}

			if(n_hh<h && n_mm<m)
			{ //看到的镜像时间 自己肯定不会去进位的呀!!! 
				if(hh<10)
					cout<<"0"<<hh<<":";
				else 
					cout<<hh<<":";
				if(mm<10)
					cout<<"0"<<mm<<endl;
				else 
					cout<<mm<<endl;
				break;
			}
		}
	} 

	return 0;
}

D:GCD of an Array

遇到求 g c d gcd gcd的题,基本上是不用写求 g c d gcd gcd的函数的,因为如果写了,必然是暴力求解~~

所以最大公约数就得考虑质因数分解

  • n n n个数的最大公约数是他们公有的质因数的最小幂次的乘积
  • 考虑用筛法筛出数据范围内的所有质因数
  • 考虑用 m a p map map维护每个 a [ i ] a[i] a[i]的质因数的幂次 ( 由 a [ i ] 的 范 围 可 知 质 因 数 并 不 多 ) (由a[i]的范围可知质因数并不多) (a[i])
  • m u l t i s e t multiset multiset维护每个质因数在每一个 a [ i ] a[i] a[i]中出现的次数,只有当某个质因数的 m u l t i s e t multiset multiset中的个数为n时,该质因数才对最后的 g c d gcd gcd有贡献
  • 现在考虑每一个 q u e r y query query,给某一个 a [ i ] a[i] a[i]乘上一个 x x x,所以可以将 x x x进行分解质因数,对于得到的每个质数 p p p的次数 c n t cnt cnt,如果 p p p没有在 a [ i ] a[i] a[i]中出现过,则直接插入到 m u l t i s e t multiset multiset中;如果出现过,则记录下 n u m = m p [ i ] [ p ] num = mp[i][p] num=mp[i][p],在 m u l t i s e t multiset multiset中找到 n u m num num并删掉,插入 c n t cnt cnt
  • 然后判断 m u l t i s e t . s i z e ( ) = = n ? multiset.size()==n? multiset.size()==n?,如果成立,则找到质数 p p p的最小幂次(注意要考虑上一次的贡献,做差),对 a n s ans ans累乘即可(优化快速幂与否均可)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

ll ans = 1;
ll mod = 1000000000+7;
const int N = 200000+10;
int n, q, nxt[N];ll a[N];
map<int,int>mp[N];
multiset<int>mt[N]; 

void add(int id,int x)
{
	while(x!=1)
	{
		int div = nxt[x], cnt = 0, mi = 0, st;
		while(nxt[x] == div)
		{
			x/=div;
			cnt++;
		}
		int i = div;
		int last = mp[id][i];
		if(last==0)
		{	
			mt[i].insert(cnt);
		}
		else 
		{
			mi = *mt[i].begin();
			multiset<int>::iterator pos = mt[i].find(last);
			mt[i].erase(pos);
			mt[i].insert(cnt+last);
		}
		
		st = *mt[i].begin();
		mp[id][i] = cnt+last;
		
		if(mt[i].size()==n)
		{
			for(int j=1;j<=st-mi;j++)
				ans = (ans*(ll)i%mod)%mod;
		}
	}
}

int main()
{
//	add(2,4*9*25*49);
ios_base::sync_with_stdio(0);
	cin>>n>>q;
	for(int i=2;i<N;i++)
	{
		if(nxt[i]==0)
		{
			nxt[i] = i;
			if(i>10000) continue;
			for(int j = i*i;j<N;j+=i)
				if(nxt[j]==0)
					nxt[j] = i;
		}
	}
//	for(int i=1;i<=1000;i++)
//		cout<<i<<" "<<nxt[i]<<endl;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		
		add(i,a[i]);
		
//		cout<<ans<<endl;
	}
	
	while(q--)
	{
		int i, x;
		cin>>i>>x;	
		add(i,x);
		cout<<ans<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值