2015编程之美复赛

第一题,不道是什么鬼。。

 

第二题猜数字。

很多用主席树,我不会,啊啊啊~~~~记得这题有出过吧,想了一发线段树的,把所有的数排序,同时把询问K排序,

做两发遍历,首先从小到大遍历所有的数,单点更新比K小的线段树的点,维护最大值,遇到>=K时则查询一发。

再从大到小遍历,更新比k大的,维护最小值,遇<=K时就查询。比较即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N= 200020;
const int inf=1000000099;
int seg[N*4];
struct No{
	int val,pos;
}node[N];

struct Qu{
	int l,r,k,index;
}query[N];
int tree_index[N];
int ans[N];
bool cmp1(No a,No b){
	if(a.val<b.val) return true;
	return false;
}
bool cmp2(Qu a,Qu b){
	if(a.k<b.k) return true;
	return false;
}
int n,q;

void build1(int rt,int l,int r){
	seg[rt]=-inf;
	if(l==r){
		tree_index[l]=rt; return ;
	}
	int mid=(l+r)>>1;
	build1(rt<<1,l,mid);
	build1(rt<<1|1,mid+1,r);
}
void update1(int rt,int val){
	seg[rt]=val;
	rt/=2;
	while(rt){
		seg[rt]=max(seg[rt<<1],seg[rt<<1|1]);
		rt/=2;
	}
}

void build2(int rt,int l,int r){
	seg[rt]=inf;
	if(l==r){
		tree_index[l]=rt; return ;
	}
	int mid=(l+r)>>1;
	build2(rt<<1,l,mid);
	build2(rt<<1|1,mid+1,r);
}
void update2(int rt,int val){
	seg[rt]=val;
	rt/=2;
	while(rt){
		seg[rt]=min(seg[rt<<1],seg[rt<<1|1]);
		rt/=2;
	}
}
int queryMax(int rt,int l,int r,int L,int R){
	if(l<=L&&R<=r){
		return seg[rt];
	}
	int mid=(L+R)>>1;
	if(l>=mid+1){
		return queryMax(rt<<1|1,l,r,mid+1,R);
	}
	else if(r<=mid) return queryMax(rt<<1,l,r,L,mid);
	return max(queryMax(rt<<1,l,r,L,mid),queryMax(rt<<1|1,l,r,mid+1,R));
}

int queryMin(int rt,int l,int r,int L,int R){
	if(l<=L&&R<=r){
		return seg[rt];
	}
	int mid=(L+R)>>1;
	if(l>=mid+1){
		return queryMin(rt<<1|1,l,r,mid+1,R);
	}
	else if(r<=mid) return queryMin(rt<<1,l,r,L,mid);
	return min(queryMin(rt<<1,l,r,L,mid),queryMin(rt<<1|1,l,r,mid+1,R));
}



int main(){
	int T,icase=0;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&q);
		for(int i=1;i<=n;i++){
			scanf("%d",&node[i].val);
			node[i].pos=i;
		}
		for(int i=1;i<=q;i++){
			scanf("%d%d%d",&query[i].l,&query[i].r,&query[i].k);
			query[i].index=i;
			ans[i]=inf;
		}
		sort(node+1,node+1+n,cmp1);
		sort(query+1,query+1+q,cmp2);
		build1(1,1,n);
		int j=1;
		for(int i=1;i<=q;i++){
			for(;node[j].val<=query[i].k&&j<=n;j++){
				update1(tree_index[node[j].pos],node[j].val);
			}
			int t=queryMax(1,query[i].l,query[i].r,1,n);
			if(t==-inf) continue;
			ans[query[i].index]=min(ans[query[i].index],query[i].k-t);
		}
//		for(int i=1;i<=q;i++)
//		printf("%d\n",ans[i]);
		j=n;
		build2(1,1,n);
		for(int i=q;i>0;i--){
			for(;node[j].val>=query[i].k&&j>0;j--){
				update2(tree_index[node[j].pos],node[j].val);
			}
			int t=queryMin(1,query[i].l,query[i].r,1,n);
			if(t==inf) continue;
			ans[query[i].index]=min(ans[query[i].index],t-query[i].k);
		}
		printf("Case #%d:\n",++icase);
		for(int i=1;i<=q;i++)
		printf("%d\n",ans[i]);
	}
	return 0;
}

  

第三题,机器人。

感谢斌神。知道,对于同一种颜色,归并后相对顺序是不对的。处理出对某个颜色相对于某颜色,所有块要移动到最后位置经过的步数,斌神叫逆序。然后,优化一下,对于某种颜色,它之前已加入K种颜色(状态压缩),它要移动到最后,经过的步数。枚举各种状态dp[k]为该状态变颜色相连要的步数,枚举状态内的颜色,求出在该状态下,该颜色移动到最末所需的最少步数,比较。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;

const LL inf=1100000000000LL;
const int MAXN=100050;
int robot[MAXN];
LL dp[1<<17];
int b[20]; LL num[20][20]; LL f[20][1<<17];
int bit[20],loc[1<<17];

int main(){
	int T,icase=0;
	bit[0]=1; loc[bit[0]]=0;
	for(int i=1;i<17;i++){
		bit[i]=(bit[i-1]*2);
		loc[bit[i]]=i;
	}
	scanf("%d",&T);
	while(T--){
		int n,k;
		scanf("%d%d",&n,&k);
		for(int i=0;i<n;i++){
			scanf("%d",&robot[i]);
			robot[i]--;
		}
		memset(b,0,sizeof(b));
		memset(num,0,sizeof(num));
		for(int i=n-1;i>=0;i--){
			for(int j=0;j<k;j++){
				if(robot[i]!=j){
					num[robot[i]][j]+=b[j];
				}
			}
			b[robot[i]]++;
		}
		int tot=(1<<k);
		for(int j=0;j<k;j++){
			f[j][0]=0;
			for(int i=1;i<tot;i++){
				int t=i&(-i);
				f[j][i]=f[j][i^t]+num[j][loc[t]];
			}
		}
		for(int i=0;i<tot;i++)
		dp[i]=inf;
		dp[0]=0;
		for(int i=0;i<tot;i++){
			if(dp[i]==inf) continue;
			for(int j=0;j<k;j++){
				if(bit[j]&i) continue;
				dp[i|bit[j]]=min(dp[i|bit[j]],dp[i]+f[j][i]);
			}
		}
		printf("Case #%d: %lld\n",++icase,dp[tot-1]);
	}
	return 0;
}

  

第四题。可以知道,若城市被包塔包围,则它至多在一个三角形内。而激活塔的费用远小于城市的,所以优先激活塔。对塔求一个凸包,看哪些点在凸包内,然后计算塔的最小环能把那些城市全包含,floyd即可,黙认为逆时针即可,计算那些城市是否在边的左边,在则可以连边,不在就不能。然后就计算即可。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;
const int MAXN=110;
const int inf=10000000;
struct Point{
	int x,y;
}cities[MAXN],tower[MAXN],anscity[MAXN];
int n,m,top,g,p;
int pexres[MAXN];

bool cmp(Point a,Point b){
	if(a.y==b.y) return a.x<b.x;
	return a.y<b.y;
}

bool multi(Point sp,Point ep,Point op){
	return (sp.x-op.x)*(ep.y-op.y)>=(ep.x-op.x)*(sp.y-op.y);
}

int Multi(Point p1,Point p2,Point p0){
	return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}

int inside(int index){
	int i; int now;
	for(i=0;i<top;i++){
		now=Multi(tower[pexres[i]],tower[pexres[(i+1)%top]],cities[index]);
		if(now<=0) return 0;
	}
	return 1;
}

void Graham(){
	int i,len;
	top=1;
	sort(tower,tower+n,cmp);
	if(n==0) return ; pexres[0]=0;
	if(n==1) return ; pexres[1]=1;
	if(n==2) return ; pexres[2]=2;
	for(i=2;i<n;i++){
		while(top&&multi(tower[i],tower[pexres[top]],tower[pexres[top-1]])) top--;
		pexres[++top]=i;
	}
	len=top;
	pexres[++top]=n-2;
	for(i=n-3;i>=0;i--){
		while(top!=len&&multi(tower[i],tower[pexres[top]],tower[pexres[top-1]])) top--;
		pexres[++top]=i;
	}
}
int d[MAXN][MAXN];

bool check(int i,int j,int cnt){
	for(int k=0;k<cnt;k++){
		int now=Multi(tower[i],tower[j],anscity[k]);
		if(now<=0) return false;
	}
	return true;
}

int main(){
	int T,icase=0;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d%d%d",&n,&m,&g,&p);
		for(int i=0;i<n;i++){
			scanf("%d%d",&tower[i].x,&tower[i].y);
		}
		for(int i=0;i<m;i++)
		scanf("%d%d",&cities[i].x,&cities[i].y);
		printf("Case #%d: ",++icase);
		if(n<3){
			printf("%d\n",g*m);
			continue;
		}
		Graham();
	//	for(int i=0;i<=top;i++)
	//	cout<<pexres[i]<<endl;
		int cnt=0;
		for(int i=0;i<m;i++){
			if(inside(i)){
				anscity[cnt++]=cities[i];
			}
		}
		int len=inf;
		if(cnt){
			for(int i=0;i<n;i++){
				for(int j=0;j<n;j++){
					d[i][j]=inf;
				}
			}
			for(int i=0;i<n;i++){
				for(int j=0;j<n;j++){
					if(i==j) continue;
					if(check(i,j,cnt)){
						d[i][j]=1;
					}
				}
			}
			for(int i=0;i<n;i++){
				for(int j=0;j<n;j++){
					for(int k=0;k<n;k++){
						d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
					}
				}
			}
			for(int i=0;i<n;i++)
			len=min(len,d[i][i]);
		}
		if(cnt==0) len=0;
		printf("%d\n",(m-cnt)*g+p*len);
	}
	return 0;
}

  

posted @ 2015-05-23 11:46  chenjunjie1994  阅读(321)  评论(0编辑  收藏  举报