Codeforces Round #595 (Div. 3) 题解

比赛链接:https://codeforces.com/contest/1249

A. Yet Another Dividing into Teams

题意:

给你n个数,问你能不能分成最少的组,使得组内任意两数之差>1。

思路:

先排序,如果存在排名相近的两个数之差<=1,则分成两组,否则一组。

代码:
#include
#include
#include
#include
#include
using namespace std;
int a[105],f[105];
int main(){
	int q;
	cin>>q;
	while(q--){
		memset(f,0,sizeof(f));
		int n,flag=0;
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		sort(a+1,a+n+1);
		for(int i=2;i<=n;i++){
			if(a[i]-a[i-1]==1){
				flag=1;
				cout<<2<<endl;
				break;
			}
		}
		if(!flag)
			cout<<1<<endl;
	}
	return 0;
} 

B1. Books Exchange (easy version)

题意:

i i i个人,第 i i i个人会在下一天把书给第 a i a_{i} ai,问每个人在第几天拿到自己的书, n < = 200 n<=200 n<=200
比如有 a = [ 5 , 1 , 2 , 4 , 3 ] a=[5,1,2,4,3] a=[5,1,2,4,3]
第一个人的书会进行如下传递:
第一天:传到 5 5 5手中
第二天:传到 3 3 3手中
第三天:传到 2 2 2手中
第四天:到 1 1 1自己手中
答案就是 4 4 4

思路:模拟
代码:
#include
#include
#include
#include
#include
using namespace std;
int a[205],f[205];
int main(){
	int q,n;
	cin>>q;
	while(q--){
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		for(int i=1;i<=n;i++){
			int t=i,ans=1;
			while(a[t]!=i){
				t=a[t];
				ans++;
			}
			cout<<ans<<' ';
		}
		cout<<endl;
	}
	return 0;
} 

B2. Books Exchange (easy version)

题意:

同上题, n < = 2 ⋅ 1 0 5 n<=2·10^5 n<=2105

思路:

求每个人所在强连通分量的大小,我用的 T a r j a n Tarjan Tarjan

代码:
#include
#include
#include
#define N 200010
using namespace std;
int low[N],dfn[N],sta[N],belong[N],a[N],num[N],index,scc,top,n;
bool insta[N];
void tarjan(int u){
	int v;
	low[u]=dfn[u]=++index;
	sta[top++]=u;
	insta[u]=true;
	v=a[u];
	if(!dfn[v]){
		tarjan(v);
		if(low[u]>low[v])
			low[u]=low[v];
	}
	else if(insta[v] && low[u]>dfn[v])
		low[u]=dfn[v];
	if(low[u]==dfn[u]){
		scc++;
		do{
			v=sta[--top];
			insta[v]=false;
			belong[v]=scc;
			num[scc]++;
		}
		while(v!=u);
	}
}
int main(){
	int q;
	cin>>q;
	while(q--){
		index=scc=top=0;
		memset(dfn,0,sizeof(dfn));
		memset(num,0,sizeof(num));
		memset(insta,0,sizeof(insta));
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%d",a+i);
		for(int i=1;i<=n;i++)
			if(!dfn[i])
				tarjan(i);
		for(int i=1;i<=n;i++)
			cout<<num[belong[i]]<<' ';
		cout<<endl;
	}
	return 0;
}

C1 - Good Numbers (easy version)

题意:

定义一个Good Number为这个数由且仅由 3 3 3的不同幂的和组成(不可重复)。
如: 30 = 3 3 + 3 1 30=3^3+3^1 30=33+31是Good Number, 19 = 3 2 + 3 3 + 3 0 = 3 2 + 3 1 + 3 1 + 3 1 + 3 0 19=3^2+3^3+3^0=3^2+3^1+3^1+3^1+3^0 19=32+33+30=32+31+31+31+30不是Good Number
要求找出第一个 > = n >=n >=n的Good Number, 1 < = n < = 1 0 4 1<=n<=10^4 1<=n<=104

思路:

打表找递推公式。

代码:
#include
#include
#include
#include
#include
using namespace std;
int ans[20000],num;
int main(){
	int q,n;
	ans[1]=1;
	for(int i=1;i<=20000;i++){
		ans[i]=(i%2==0?ans[i/2]*3:ans[i-1]+1);
	}
	cin>>q;
	while(q--){
		int n;
		cin>>n;
		for(int i=1;i<=num;i++)
			if(ans[i]>=n){
				cout<<ans[i]<<endl;
				break;
			}
	}
	return 0;
} 

C2 - Good Numbers (hard version)

题意:

如上,但 n n n的范围为 1 < = n < = 1 0 18 。 1<=n<=10^{18}。 1<=n<=1018

思路:

上题中ans[i]=(i%2==0?ans[i/2]*3:ans[i-1]+1);
根据上式发现当 i = 2 x i=2^x i=2x时, a [ i ] = 3 x a[i]=3^x a[i]=3x i i i为奇数时, a [ i ] = a [ i − 1 ] a[i]=a[i-1] a[i]=a[i1],则存在这样一条性质。即 a [ i ] = a [ i − l o w b i t ( i ) ] + a [ l o w b i t ( i ) ] a[i]=a[i-lowbit(i)]+a[lowbit(i)] a[i]=a[ilowbit(i)]+a[lowbit(i)],则可以用 l o g n log{n} logn的时间求 a [ i ] a[i] a[i],通过二分法查找第一个 > = n >=n >=n i i i,就可以解决此题。

代码:

时间复杂度 ( l o g n ) 2 {(log{n}})^2 (logn)2

#include
#include
#include
#include
#include
#define ll long long
using namespace std;
inline ll qpow(ll a, ll n)
{
    ll ans = 1;
    while(n)
    {
        if(n & 1) ans = ans * a;
        a = a * a;
        n >>= 1;
    }
    return ans;
}
inline ll lowb(ll x){
	return x&-x;
}
inline ll get2(ll x){
	ll t=-1;
	while(x){
		x>>=1;
		t++;
	}
	return t;
} 
inline ll f(ll x){
	if(x==1)
		return 1;
	if(lowb(x)==x){
		return qpow(3,get2(x));
	}
	return f(x-lowb(x))+f(lowb(x));
}
ll bserch(ll l,ll r,ll v){
	while(l<=r){
		ll mid=(l+r)/2;
		ll p=f(mid);
		if(p<v)
			l=mid+1;
		else if(p>v)
			r=mid-1;
		else {
			return mid;
		}
	}
	return l;
}
int main(){
	ll n,pos;
	int q;
	cin>>q;
	while(q--){
		scanf("%lld",&n);
		pos=bserch(1,min(n,274877906944),n);//打表找出1^18对应的i
		cout<<f(pos)<<endl;
	}
	return 0;
} 

D. Too Many Segments

题意:

删除最少的线段,使得坐标轴上每个点被线段覆盖的次数<=k

思路:

贪心地对于点 p p p,如果覆盖 p p p的线段 > k >k >k个,则删除覆盖p且右端点最大的线段。
用set维护覆盖点 i i i的线段,做法是随时弹出 S S S内右端点 < i <i的线段,并加入以 i i i为左端点的线段。

代码:
#include
#include
#include
#include
#define N 200005
#define pii pair
using namespace std;
vector<pii> pnt[N];
vector<int>ans;
set<pii> s;
int main(){
	int n,k,R=0,L=0x3f3f3f,l,r;
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		scanf("%d %d",&l,&r);
		pnt[l].push_back(make_pair(r,i));
		R=max(R,r);
		L=min(L,l);
	}
	for(int i=L;i<=R;i++){
		while(!s.empty() && s.begin()->first<i)
			s.erase(s.begin());
		for(int j=0;j<pnt[i].size();j++)
			s.insert(pnt[i][j]);
		while(s.size()>k){
			ans.push_back(prev(s.end())->second);
			s.erase(prev(s.end()));
		}
	}
	cout<<ans.size()<<endl;
	for(int i=0;i<ans.size();i++)
		cout<<ans[i]<<' ';
	return 0;
}

你可能感兴趣的:(Codeforces,Round)