CCO2017题解
为数不多的可做题之一……
考虑题目要求要用较少的边构造很多的点对,观察样例,容易想到环。
一个大小为\(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",n,m);
int num=1;
for(int i=1;i<=top;i++){
if(num>1)printf("%d %d\n",num-1,num);
for(int j=num;j<num+b[i]-1;j++)
printf("%d %d\n",j,j+1);
printf("%d %d\n",num+b[i]-1,num);
num+=b[i];
}
return 0;
}
正解=暴搜?复杂度=玄学?对,就是这样。
直接暴搜+一点点记忆化即可,注意有的地方可以稍微贪心一下。
贪心时注意留足保证正确性的余地。
欢迎各位大佬踊跃证明复杂度,反正我是不太会。
#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\n",mn(n,m),mx(n,m));
return 0;
}
大常数的\(map+01trie\),评测机就是用来信仰的。
对每个\(x_i\)建一棵\(01trie\),存放\(y_i\),不同的是第\(i\)层存放\(2^i\)(这样的好处是高位都可以被模掉)。
对每个\(r_i\)暴力枚举二的幂次,然后在\(01trie\)里查询即可。
真不明白\(\text{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\n",v);
}
return 0;
}
考虑将高度从大到小排序。
设\(f[i][j]\)表示用了前\(i\)块地,柱子和水的体积和为\(j\)合不合法。
用\(\text{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;
}
这题挺难的,\(\text{luogu}\)只评绿,实在搞不懂。
按代价排序,贪心地选取。
若第\(i\)个人想免费,那么\(\text{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("\n");
}
printf("%d\n",ans);
return 0;
}
全网无题解,至今不会做。