APIO2015 UOJ 110-112

25 篇文章 0 订阅
16 篇文章 0 订阅
很像NOI2014的起床困难综合征
首先每一位拆开来做
从高位到低位贪心,看每一位能不能为0
N<=100的时候可以搞一个判定性DP
N<=2000的时候由于A=1,考虑最优化问题,最小化组数,看是不是小于等于b
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int inf=1e9;
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
#define mmt(a,v) memset(a,v,sizeof(a))
#define tra(i,u) for(int i=head[u];i;i=e[i].next)
const int N=2000+5;
typedef long long ll;
ll s[N],bin[60];
int n,a,b;
ll change(ll x,int t){
	return (x>>t)<<t;
}
bool sub(ll s,ll ans){
	return (s|ans)==ans;
}
namespace solve1{
	bool f[105][105],g[105][105];
	int main(){
		rep(i,1,n)
		rep(j,i,n)f[i][j]=1;
		ll ans=0;
		per(t,40,0){
			memcpy(g,f,sizeof(f));
			rep(i,1,n)f[1][i]&=sub(change(s[i],t),ans);
			rep(i,2,n)
			rep(j,i,n){
				bool flag=false;
				rep(k,i-1,j-1){
				flag|=f[i-1][k]&&sub(change(s[j]-s[k],t),ans);
				}
				f[i][j]&=flag;
			}
			bool flag=false;
			rep(i,a,b)flag|=f[i][n];
			if(!flag)memcpy(f,g,sizeof(f)),ans|=bin[t];
			//rep(i,1,n)
			//rep(j,1,n)
			//printf("%d %d %d %d\n",t,i,j,f[i][j]);
		}
		printf("%lld\n",ans);
		return 0;
	}
}
namespace solve2{
	ll ans;
	int f[N];
	int main(){
		per(t,40,0){
			mmt(f,0x3f);
			f[0]=0;
			rep(i,1,n)
			rep(j,0,i-1)
			if(sub(change(s[i]-s[j],t),ans))
			f[i]=min(f[i],f[j]+1);
			if(f[n]>b)ans|=bin[t];
		}
		printf("%lld\n",ans);
		return 0;
	}
}
int main(){
	//freopen("a.in","r",stdin);
	bin[0]=1;rep(i,1,50)bin[i]=bin[i-1]<<1;
	scanf("%d%d%d",&n,&a,&b);
	rep(i,1,n)scanf("%lld",&s[i]),s[i]+=s[i-1];
	if(n<=100)return solve1::main();
	else return solve2::main();
	return 0;
}
暴力跑Dijkstra+一个小优化大概有57分
考虑分块,由于p大于sqrt(N)的点,连出去的边最多sqrt(N)条,暴力加边
而剩下的点,假设p1,p2,p3为三栋建筑,p1到p2距离等于p2到p3距离,假设p1到p2有边且p2到p3有边,那么p1到p3的那条边就不用连了,于是每一种类型的边最多N条,类型最多sqrt(N)种,边数Nsqrt(N)
然后跑最短路就好了
不知道为啥被卡了3分RE(疑似线段树写挂了)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int inf=1e9;
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
#define mmt(a,v) memset(a,v,sizeof(a))
#define tra(i,u) for(int i=head[u];i;i=e[i].next)
const int N=30000+5;
int d[N<<1];
int tr[N<<2],M;
bool done[N];
bool cmp(int i,int j){
	return d[i]<d[j];
}
void update(int n,int v){
	if(d[n]<=v)return;
	d[n]=v;
	for(n+=M,n>>=1;n;n>>=1)
	tr[n]=min(tr[n<<1],tr[n<<1|1],cmp);
}
void modify(int n,int v){
	d[n]=v;
	for(n+=M,n>>=1;n;n>>=1)
	tr[n]=min(tr[n<<1],tr[n<<1|1],cmp);
}
void build(){
	per(i,M-1,1)tr[i]=min(tr[i<<1],tr[i<<1|1],cmp);
}
struct Edge{int to,next,v;}e[N*200];
int head[N],cnt;
void ins(int u,int v,int w){e[++cnt]=(Edge){v,head[u],w};head[u]=cnt;}
int dijkstra(int s,int t){
	mmt(d,0x3f);d[s]=0;build();
	while(d[tr[1]]<inf){
		int u=tr[1];
		if(u==t)return d[u];
		done[u]=true;
		tra(i,u){
			int v=e[i].to;if(done[v])continue;
			update(v,d[u]+e[i].v);
		}
		modify(u,inf);
	}
	return -1;
}
struct doge{
	int pos,step;
	bool operator < (const doge &x)const{
		return pos==x.pos?step<x.step:pos<x.pos;
	}
}dog[N];
bool have[N],step[105][N];
int main(){
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	int n,m;
	scanf("%d%d",&n,&m);
	M=1;
	while(M-2<n)M<<=1;
	rep(i,1,n)tr[i+M]=i;
	rep(i,1,m){
		scanf("%d%d",&dog[i].pos,&dog[i].step);dog[i].pos++;have[dog[i].pos]=true;
		if(dog[i].step<=100)step[dog[i].step][dog[i].pos]=true;
	}
	int s=dog[1].pos,t=dog[2].pos;
	sort(dog+1,dog+1+m);
	int top=1;
	rep(i,2,m)if(dog[i].pos!=dog[top].pos||dog[i].step!=dog[top].step)dog[++top]=dog[i];
	m=top;
	rep(i,1,m){
		int p=dog[i].pos,l=dog[i].step;
		if(l>100){
			for(int j=1,now=p+l;now<=n;now+=l,j++)if(have[now])ins(p,now,j);
			for(int j=1,now=p-l;now>0;now-=l,j++)if(have[now])ins(p,now,j);
		}else{
			for(int j=1,now=p+l;now<=n;now+=l,j++)if(have[now]){ins(p,now,j);if(step[l][now])break;}
			for(int j=1,now=p-l;now>0;now-=l,j++)if(have[now]){ins(p,now,j);if(step[l][now])break;}
		}
	}
	printf("%d\n",dijkstra(s,t));
	return 0;
}
显然对于K=1的情况直接找坐标中位数即可
对于K=2的情况我们可以考虑分为两个K=1的情况
以每一对节点的中点为序进行排序,然后枚举分割点,分割点之前的都走桥1,分割点之后的都走桥2
利用权值线段树/平衡树/树状数组/堆维护中位数信息,O(logn)找到桥1桥2的位置以及点到他们的距离和。
取最小值即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int inf=1e9;
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
#define mmt(a,v) memset(a,v,sizeof(a))
#define tra(i,u) for(int i=head[u];i;i=e[i].next)
const int N=100000+5;
typedef long long ll;
ll iabs(ll x){return x>0?x:-x;}
ll x[N],y[N],base;
int n,k;
namespace solve1{
	ll pos[N<<1];
	void main(){
		rep(i,1,n)pos[i<<1]=x[i],pos[2*i-1]=y[i];
		sort(pos+1,pos+1+2*n);
		ll ans=0,p=pos[n];
		rep(i,1,2*n)
		ans+=iabs(p-pos[i]);
		printf("%lld\n",ans+base+n);
	}
}
namespace solve2{
	struct point{
		ll x,y,m;
		bool operator < (const point &p)const{
			return m<p.m;
		}
	}p[1005];
	ll pos[2005];
	ll work(int m){
		if(m<=1)return 0;
		sort(pos+1,pos+1+m);
		ll p=pos[(m+1)>>1];
		ll ans=0;
		rep(i,1,m)ans+=iabs(p-pos[i]);
		return ans;
	}
	void main(){
		rep(i,1,n)p[i]=(point){x[i],y[i],x[i]+y[i]>>1};
		sort(p+1,p+1+n);
		ll ans=1LL<<60;
		rep(i,0,n){
			ll tmp=0;
			int top=0;
			rep(j,1,i)pos[++top]=p[j].x,pos[++top]=p[j].y;
			tmp+=work(top);
			top=0;
			rep(j,i+1,n)pos[++top]=p[j].x,pos[++top]=p[j].y;
			tmp+=work(top);
			ans=min(ans,tmp);
		}
		if(!n)ans=0;
		printf("%lld\n",ans+base+n);
	}
}
namespace solve3{
	struct point{
		ll x,y,m;
		bool operator < (const point &p)const{
			return m<p.m;
		}
	}p[N];
	ll hash[N<<1];
	struct Node{
		int l,r,sz;
		ll sum;
	};
	struct segment_tree{
		Node tr[N<<3];
		#define lc o<<1
		#define rc o<<1|1
		void build(int o,int l,int r){
			tr[o].l=l;tr[o].r=r;
			if(l==r)return;
			int mid=l+r>>1;
			build(lc,l,mid);build(rc,mid+1,r);
		}
		void pushup(int o){tr[o].sz=tr[lc].sz+tr[rc].sz;tr[o].sum=tr[lc].sum+tr[rc].sum;}
		void insert(int o,int p){
			int l=tr[o].l,r=tr[o].r;
			if(l==r)tr[o].sz++,tr[o].sum+=hash[p];
			else{
				int mid=l+r>>1;
				if(p<=mid)insert(lc,p);
				else insert(rc,p);
				pushup(o);
			}
		}
		void del(int o,int p){
			int l=tr[o].l,r=tr[o].r;
			if(l==r)tr[o].sz--,tr[o].sum-=hash[p];
			else{
				int mid=l+r>>1;
				if(p<=mid)del(lc,p);
				else del(rc,p);
				pushup(o);
			}
		}
		int find(int o,int k){
			int l=tr[o].l,r=tr[o].r;
			if(l==r)return l;
			else if(k<=tr[lc].sz)return find(lc,k);
			else return find(rc,k-tr[lc].sz);
		}
		ll sum(int o,int k){
			int l=tr[o].l,r=tr[o].r;
			if(l==r)return hash[l]*k;
			else if(k<=tr[lc].sz)return sum(lc,k);
			else return tr[lc].sum+sum(rc,k-tr[lc].sz);
		}
		ll calc(){
			if(!tr[1].sz)return 0;
			int k=tr[1].sz+1>>1,p=find(1,k);
			return hash[p]*k-2*sum(1,k)+tr[1].sum-hash[p]*(tr[1].sz-k);
		}
	}t1,t2;
	int m;
	int find(ll x){
		return lower_bound(hash+1,hash+1+m,x)-hash;
	}
	void main(){
		rep(i,1,n)p[i]=(point){x[i],y[i],x[i]+y[i]>>1},hash[2*i-1]=x[i],hash[2*i]=y[i];
		sort(p+1,p+1+n);sort(hash+1,hash+1+2*n);
		m=unique(hash+1,hash+1+2*n)-hash-1;
		t1.build(1,1,m);t2.build(1,1,m);
		ll ans=n?(1LL<<60):0;
		rep(i,1,n)p[i].x=find(p[i].x),p[i].y=find(p[i].y);
		rep(i,1,n)t2.insert(1,p[i].x),t2.insert(1,p[i].y);
		ans=min(ans,t2.calc());
		rep(i,1,n){
			t2.del(1,p[i].x);t2.del(1,p[i].y);
			t1.insert(1,p[i].x);t1.insert(1,p[i].y);
			ans=min(ans,t1.calc()+t2.calc());
		}
		printf("%lld\n",ans+n+base);
	}
}
int main(){
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	scanf("%d%d",&k,&n);
	char f1[5],f2[5];
	rep(i,1,n){
		scanf("%s %lld %s %lld",f1,&x[i],f2,&y[i]);
		if(f1[0]==f2[0])base+=iabs(x[i]-y[i]),i--,n--;
	}
	if(k==1)solve1::main();
	else if(n<=1000)solve2::main();
	else solve3::main();
	return 0;
}

感觉题略难与2014的(2014题实在太水了)
不知道今年的举办国是哪个啊,感觉有点虚
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值