CCO2017题解

#2750. 「CCO 2017」Vera 与道路建设

为数不多的可做题之一……

考虑题目要求要用较少的边构造很多的点对,观察样例,容易想到环。

一个大小为(n)的环,有(frac12 n(n-1))对点。

则解法就是构造一堆环,然后用边连起来就好了(题目要求为连通图)。

时间复杂度(Theta(n+sqrt n))

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
int k,n,m=-1,a[maxn],b[maxn],top;
inline 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-'0';c=getchar();}
	return x*f;
}
inline int divide(int val){
	int l=1,r=10000,res=1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(a[mid]<=val)res=mid,l=mid+1;
		else r=mid-1;
	}
	return res;
}
int main(){
	for(int i=1;i<=10000;i++)
		a[i]=i*(i-1)/2;
	k=read();
	while(k!=0){
		b[++top]=divide(k);
		k-=a[b[top]];
		n+=b[top],m+=b[top]+1;
	}
	printf("%d %d
",n,m);
	int num=1;
	for(int i=1;i<=top;i++){
		if(num>1)printf("%d %d
",num-1,num);
		for(int j=num;j<num+b[i]-1;j++)
			printf("%d %d
",j,j+1);
		printf("%d %d
",num+b[i]-1,num);
		num+=b[i];
	}
	return 0;
}

#2751. 「CCO 2017」矩形帝国的霸业

正解=暴搜?复杂度=玄学?对,就是这样。

直接暴搜+一点点记忆化即可,注意有的地方可以稍微贪心一下。

贪心时注意留足保证正确性的余地。

欢迎各位大佬踊跃证明复杂度,反正我是不太会。

#include<bits/stdc++.h>
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline void swap(int &x,int &y){int t=x;x=y;y=t;}
#define inf 1e9
const int maxn=5e3+10;
const int mod=1e9+7;
const int T=5e3+5;
int n,m,f[maxn][maxn],g[maxn][maxn];
inline 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-'0';c=getchar();}
	return x*f;
}
inline int mn(int n,int m){
	if(n<m)swap(n,m);
	if(m==0)return 0;
	if(m==1)return (n&1)?inf:n/2;
	if(n<=T&&f[n][m])return f[n][m];
	int ans=inf;
	if(n<4*m){
		if(n%2==0&&m>=n/2)ans=min(ans,mn(n,m-n/2)+1);
		if(n>=m*2)ans=min(ans,mn(n-2*m,m)+1);
		if(m%2==0)ans=min(ans,mn(n-m/2,m)+1);
	}else ans=min(ans,mn(n%(2*m)+2*m,m)+n/(2*m)-1);
	if(n<=T)f[n][m]=ans;
	return ans;
}
inline int mx(int n,int m){
	if(n<m)swap(n,m);
	if(m==0)return 0;
	if(m==1)return (n&1)?-inf:n/2;
	if(n<=T&&g[n][m])return g[n][m];
	int ans=-inf;
	if(n>=2*m){
		if(m%2==0)return mx(n%(m/2)+3*m/2,m)+n/(m/2)-3;
		return mx(n%(2*m),m)+n/(2*m);
	}else{
		if(n%2==0)ans=max(ans,mx(n,m-n/2)+1);
		if(m%2==0)ans=max(ans,mx(n-m/2,m)+1);
		if(n<=T)g[n][m]=ans;
		return ans;
	}
}
int main(){
	n=read(),m=read();
	printf("%d %d
",mn(n,m),mx(n,m));
	return 0;
}

#2752. 「CCO 2017」Vera 与现代艺术

大常数的(map+01trie),评测机就是用来信仰的。

对每个(x_i)建一棵(01trie),存放(y_i),不同的是第(i)层存放(2^i)(这样的好处是高位都可以被模掉)。

对每个(r_i)暴力枚举二的幂次,然后在(01trie)里查询即可。

真不明白( ext{luogu})为什么给一个没人做的毒瘤卡常题评蓝,太低了吧。

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=1e7+3e6+10;
const int mod=1e9+7;
typedef long long ll;
int n,m;
inline int hb(ll x){
	for(int i=60;~i;--i)
		if(x>>i&1)return i;
}
map<pair<ll,int>,int>rt;
int ch[2][maxn],val[maxn],tot;

int main(){
	scanf("%d%d",&n,&m);ll x,y;
	for(int i=1,v;i<=n;++i){
		scanf("%lld%lld%d",&x,&y,&v);
		int u=rt[{x^(1ll<<hb(x)),hb(x)}];
		if(!u)rt[{x^(1ll<<hb(x)),hb(x)}]=u=++tot;
		for(int j=0,k=hb(y);j<k;++j){
			if(!ch[y>>j&1][u])ch[y>>j&1][u]=++tot;
			u=ch[y>>j&1][u];
		}
		val[u]+=v;
	}
	for(int i=1,v;i<=m;++i){
		scanf("%lld%lld",&x,&y);v=0;
		for(int j=0,j1=hb(x);j<=j1;++j)
			if(rt.count({x&((1ll<<j)-1),j}))
			for(int k=0,k1=hb(y),u=rt[{x&((1ll<<j)-1),j}];u&&k<=k1;++k)
				v+=val[u],u=ch[y>>k&1][u];
		printf("%d
",v);
	}
	return 0;
}

#2753. 「CCO 2017」接雨滴

考虑将高度从大到小排序。

(f[i][j])表示用了前(i)块地,柱子和水的体积和为(j)合不合法。

( ext{bitset})暴力转移即可。

具体内容细品代码。

#include <bits/stdc++.h>
using namespace std;
const int N=505;
const int M=30005;
bitset<M>f[N],ans;
int n,a[N],sum;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)sum+=a[i];
    sort(a+1,a+n+1,greater<int>());
    f[1][a[1]]=1;
    for(int i=2;i<=n;i++){
        for(int j=i;j<=n;j++)
	      f[j]|=f[j-1]<<a[i];
        ans|=f[n];
    }
    for(int i=sum;i<M;i++)
	if(ans[i])printf("%d ",i-sum);
    return 0;
}

#2754. 「CCO 2017」专业网络

这题挺难的,( ext{luogu})只评绿,实在搞不懂。

按代价排序,贪心地选取。

若第(i)个人想免费,那么( ext{Ta})一定是第([a_i+1,n])个被结识的。

如果这个区间已经占满了,则付费,否则选取最左侧的空位。

正确性显然,线段树维护即可。

#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
int n,tr[maxn<<2],ans;
inline 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-'0';c=getchar();}
	return x*f;
}
struct node{int a,b;}p[maxn];
inline int cmp(node x,node y){return x.b>y.b;}
inline int update(int h,int l,int r,int x,int y){
	if(tr[h]==r-l+1)return 0;
	if(l>y||r<x)return 0;
	//if(l==r)printf(" %d",l);
	if(l==r)return tr[h]=1;
	if(l>=x&&r<=y){
		int mid=(l+r)>>1;
		if(tr[h<<1]<mid-l+1)update(h<<1,l,mid,x,y);
		else update(h<<1|1,mid+1,r,x,y);
		tr[h]=tr[h<<1]+tr[h<<1|1];
		return 1;
	}
	int mid=(l+r)>>1;
	int flag=update(h<<1,l,mid,x,y);
	if(!flag)flag|=update(h<<1|1,mid+1,r,x,y);
	tr[h]+=flag;
	return flag;
}
inline int query(int h,int l,int r,int x,int y){
	if(l>y||r<x)return 0;
	if(l>=x&&r<=y)return tr[h];
	int mid=(l+r)>>1;
	return query(h<<1,l,mid,x,y)+query(h<<1|1,mid+1,r,x,y);
}
int main(){
	n=read();
	for(int i=1;i<=n;i++)
		p[i].a=read(),p[i].b=read();
	sort(p+1,p+1+n,cmp);
	for(int i=1;i<=n;i++){
		//printf("%d:",p[i].b);
		ans+=(1-update(1,1,n,p[i].a+1,n))*p[i].b;
		//puts("
");
	}
	printf("%d
",ans);
	return 0;
}

#2755. 「CCO 2017」移动数组

全网无题解,至今不会做。

原文地址:https://www.cnblogs.com/syzf2222/p/14389148.html