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;
}

  

时间: 2024-10-02 09:37:05

2015编程之美复赛的相关文章

[2015编程之美] 第一场A

#1156 : 彩色的树 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 给定一棵n个节点的树,节点编号为1, 2, …, n.树中有n - 1条边,任意两个节点间恰好有一条路径.这是一棵彩色的树,每个节点恰好可以染一种颜色.初始时,所有节点的颜色都为0.现在需要实现两种操作: 1. 改变节点x的颜色为y: 2. 询问整棵树被划分成了多少棵颜色相同的子树.即每棵子树内的节点颜色都相同,而相邻子树的颜色不同. 输入 第一行一个整数T,表示数据组数,以下是T组数据. 每组

[2015编程之美] 第一场C

题目3 : 质数相关 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 两个数a和 b (a<b)被称为质数相关,是指a × p = b,这里p是一个质数.一个集合S被称为质数相关,是指S中存在两个质数相关的数,否则称S为质数无关.如{2, 8, 17}质数无关,但{2, 8, 16}, {3, 6}质数相关.现在给定一个集合S,问S的所有质数无关子集中,最大的子集的大小. 输入 第一行为一个数T,为数据组数.之后每组数据包含两行. 第一行为N,为集合S的大小.第二行为

[2015编程之美] 资格赛C

#1150 : 基站选址 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 需要在一个N × M的网格中建立一个通讯基站,通讯基站仅必须建立在格点上. 网格中有A个用户,每个用户的通讯代价是用户到基站欧几里得距离的平方. 网格中还有B个通讯公司,维护基站的代价是基站到最近的一个通讯公司的路程(路程定义为曼哈顿距离). 在网格中建立基站的总代价是用户通讯代价的总和加上维护基站的代价,最小总代价. 输入 第一行为一个整数T,表示数据组数. 每组数据第一行为四个整数:N, M

2015编程之美初赛第三题质数相关

1.解题思路 按题要求,输入数据后先进行是否有两两相同的数据的判断,接着进行从小到大的排序,由于是求质数无关,则集合中任何一对数都不能质数相关,本人采用顺序遍历,将集合中每个数字分为必须包含和必须排外两种情况遍历,首先集合第一个数必须包含,且作为子集中第一个元素,由于是有序数据,只需从集合第二个数遍历起,对加人子集的每个数做质数相关判断(需要遍历一遍),若与子集每个数都质数无关,则将该数纳入该子集,直到遍历完集合后续所有数据,完了统计子集大小:再来第二遍遍历,这是最外循环从大集合第二个数据起,此

2015编程之美初赛第一场 A 彩色的树

时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 给定一棵n个节点的树,节点编号为1, 2, -, n.树中有n - 1条边,任意两个节点间恰好有一条路径.这是一棵彩色的树,每个节点恰好可以染一种颜色.初始时,所有节点的颜色都为0.现在需要实现两种操作: 1. 改变节点x的颜色为y: 2. 询问整棵树被划分成了多少棵颜色相同的子树.即每棵子树内的节点颜色都相同,而相邻子树的颜色不同. 输入 第一行一个整数T,表示数据组数,以下是T组数据. 每组数据第一行是n,表示树的节

2015编程之美 2月29日(求闰年的个数)

<span style="font-size:14px;">// 描述 // 给定两个日期,计算这两个日期之间有多少个2月29日(包括起始日期). // 只有闰年有2月29日,满足以下一个条件的年份为闰年: // 1. 年份能被4整除但不能被100整除 // 2. 年份能被400整除 // 输入 // 第一行为一个整数T,表示数据组数. // 之后每组数据包含两行.每一行格式为"month day, year",表示一个日期.month为{"J

2015编程之美 彩色的树

题目1 : 彩色的树 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 给定一棵n个节点的树,节点编号为1, 2, -, n.树中有n - 1条边,任意两个节点间恰好有一条路径.这是一棵彩色的树,每个节点恰好可以染一种颜色.初始时,所有节点的颜色都为0.现在需要实现两种操作: 1. 改变节点x的颜色为y: 2. 询问整棵树被划分成了多少棵颜色相同的子树.即每棵子树内的节点颜色都相同,而相邻子树的颜色不同. 输入 第一行一个整数T,表示数据组数,以下是T组数据. 每组数据

2015编程之美初赛第二场扑克牌

一副不含王的扑克牌由52张牌组成,由红桃.黑桃.梅花.方块4组牌组成,每组13张不同的面值.现在给定52张牌中的若干张,请计算将它们排成一列,相邻的牌面值不同的方案数. 牌的表示方法为XY,其中X为面值,为2.3.4.5.6.7.8.9.T.J.Q.K.A中的一个.Y为花色,为S.H.D.C中的一个.如2S.2H.TD等. 输入 第一行为一个整数T,为数据组数. 之后每组数据占一行.这一行首先包含一个整数N,表示给定的牌的张数,接下来N个由空格分隔的字符串,每个字符串长度为2,表示一张牌.每组数

2015编程之美初赛第一场 B 建造金字塔

 时间限制:4000ms 单点时限:2000ms 内存限制:256MB 描述 在二次元中,金字塔是一个底边在x轴上的等腰直角三角形. 你是二次元世界的一个建筑承包商.现在有N个建造订单,每个订单有一个收益w,即建造此金字塔可获得w的收益.对每个订单可以选择建造或不建造. 建造一个金字塔的成本是金字塔的面积,如果两个或多个金字塔有重叠面积,则建造这些金字塔时重叠部份仅需建造一次. 建造一组金字塔的总利润是收益总和扣除成本.现给出这些订单,请求出最大利润. 输入 输入数据第一行为一个整数T,表示