[noip模拟赛2017.7.13]

全国信息学分区联赛模拟试题(五)

【试题概览】

试题名称 序列 矩形 看守
提交文件 sequence rectangle lock jail
输入文件 sequence.in rectangle.in lock.in jail.in
输出文件 sequence.out rectangle.out lock.out jail.out 时间限制
空间限制 128MB 128MB 128MB 128MB

题目来源 TopCoder

序列

【题目描述】

有一个整数序列,它的每个数各不相同,我们不知道它的长度是多少(即整数个数),但我 们知道在某些区间中至少有多少个整数,用区间(Li,Ri,Ci)来描述,表示这个整数序列中至 少有 Ci 个数来自区间[Li,Ri],给出若干个这样的区间,问这个整数序列的长度最少能为多 少?

【输入文件】

第一行一个整数 N,表示区间个数; 接下来 N 行,每行三个整数(Li,Ri,Ci),描述一个区间。

【输出文件】

仅一个数,表示该整数序列的最小长度。

【样例输入】

4

4 5 1

6 10 3

7 10 3

5 6 1

【样例输出】

4

【数据规模】

N<=1000,0<=Li<=1000,1<=Ci<=Ri-Li+1

题解

考虑一下前缀和,对于每个每个区间(Li,Ri,Ci),那么sum[Li-1]+Ci>=sum[Ri],然后把每个这样的区间看作一条边,建图,用差分约束的思想跑SPFA,答案即为最小的数

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;

int n;
struct Edge{
	int a,b,d,nt;
	Edge(int aa=0,int bb=0,int dd=0,int nn=0)
		{a=aa;b=bb;d=dd;nt=nn;}
}e[100000];int p[100000],cnt=1;
void add(int x,int y,int w){
	e[cnt]=Edge(x,y,w,p[x]);
	p[x]=cnt++;
}
int a[2010],c[1010];

int dis[100000];
bool vis[100000];
queue<int>q;
int spfa(){
	memeset(dis,0,sizeof(dis));
	q.push(a[2]);
	dis[a[2]]=0;
	while(!q.empty()){
		int k=q.front();q.pop();vis[k]=false;
		for(int i=p[k];i;i=e[i].nt){
			int kk=e[i].b;
			if(dis[kk]<dis[k]+e[i].d){
				dis[kk]=dis[k]+e[i].d;
				if(!vis[kk]){
					vis[kk]=true;
					q.push(kk);
				}
			}
		}
	}
	return dis[a[n<<1|1]];
}
int main(){
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d",&a[i<<1],&a[i<<1|1],&c[i]);
		a[i<<1|1]++;
		add(a[i<<1],a[i<<1|1],c[i]);
	}
	sort(a+2,a+2+(n<<1));
	for(int i=2;i<(n<<1|1);i++){
		add(a[i],a[i+1],0);
		add(a[i+1],a[i],a[i]-a[i+1]);
		add(a[2],a[i+1],0);
	}
	printf("%d",spfa());
} 

/*
4
4 5 1
6 10 3
7 10 3
5 6 1
*/

矩形

【题目描述】

给你个 01 矩阵,问共有多少个不同的全 0 矩阵?

【输入文件】

第一行两个整数 N,M; 接下来 N 行,每行 M 个字符,如果字符为‘.’,表示这格可行(即表示数字 0); 如果为'×',表示不可行(即表示数字 1)。

【输出文件】

一个数表示答案。

【样例输入】

6 4
....
.×××
.×..
.×××
...×
.×××

【样例输出】

38

【数据规模】

30%的数据,N,M<=50; 100%的数据,N,M<=200.

题解

暴力直接过,枚举矩形上下界,O(m)统计确定上下界的矩形个数,O(mn^2)跑出正解

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

int n,m,ans;
bool mp[500][500];
bool fail[500];
char s[500];
int main(){
	freopen("ectangle.in","r",stdin);
	freopen("ectangle.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%s",s);
		for(int j=1;j<=m;j++)
			mp[i][j]=(s[j-1]=='.'?true:false);
	}
	for(int up=1;up<=n;up++){
		memset(fail,0,sizeof(fail));
		for(int down=up;down<=n;down++){
			for(int i=1;i<=m;i++)
				if(!mp[down][i])fail[i]=true;
			for(int l=1;l<=m;l++){
				int r=l;
				while(!fail[r]&&r<=m)r++;
				ans+=((r-l+1)*(r-l))>>1;
				l=r;
			}
		}
	}
	printf("%d",ans);	
}
/*
6 4
....
.xxx
.x..
.xxx
...x
.xxx
*/

【题目描述】

给出 N 和 K,要求生成从 0 到 2^N-1 的一个序列,序列的第一项为 0,并且该序 列满足以下三个条件: (1)序列长度为 2^N,保证 0 到 2^N-1 每个数都用了且只用了一次。 (2)序列中任意两相邻的数都是由前一个数在其二进制下,改变了具有相同值 的若干位而形成的,即把其中若干个 0 变为 1,或把其中若干个 1 变成 0,并且 只能 2 选 1. (3)当存在多个序列满足前两个条件的时候,要保证字典序最小,即由前一个 数生成后一个数的时候,要挑值最小的数(当然是满足前两个条件的情况下)。 现在问你这个序列前 K 项中的最大值是多少,输出其二进制形式,注意一定要 输出 N 位,包括前导零。

【输入文件】

仅一行,两个整数 N,K。

【输出文件】

一个二进制的数,为所求的答案。

【样例输入】

3 8

【样例输出】

111

【样例解释】

整个序列为“000”,“001”,“011”,“010”,“110”,“100”,“101”,“111”。

【数据规模】

1<=N<=50,1<=K<=2^N,注意 K 可能超过 longint。

题解

玄学比较,玄学混分,大概就是找规律吧,多写几个,写到20左右一般就能看出规律了,然后就xjb乱搞,还没弄懂证明就A了

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

LL K,n;
LL l,r;
LL dfs(int p,LL k){
	if(p<0)return 0;
	LL half=(1l<<p);
	if(k<=half)return dfs(p-1,k);
	else if(k==half+1)return dfs(p-1,half)+half;
	else return dfs(p-1,k-half-1)+half;
}
LL Max(int p,LL k){
	if(p<0)return 0;
	LL half=(1ll<<p);
	if(k<=half)return Max(p-1,k);
	LL now=dfs(p,half+1);
	if(k==half+1)return now;
	else return max(now,Max(p,k-half-1)+half);
}
int main(){
	freopen("lock.in","r",stdin);
	freopen("lock.out","w",stdout);
	scanf("%d%lld",&n,&K);
	LL ans=Max(n-1,K);
	for(int i=n-1;i>=0;i--)
		cout<<(((1ll<<i)&ans)?"1":"0");
}

看守

【题目描述】

给出 D 维空间的 N 个点,求曼哈顿距离最大的两个点的曼哈顿距离。两个 D 维 的点(x1,x2,...xD),(y1,y2,...yD)的曼哈顿距离定义为

sum_{i=1}^{D}|x_i-y_i|

【输入文件】

第一行两个整数 N,D; 接下来 N 行,每行 D 个整数描述一个点的坐标。

【输出文件】

输出最大的曼哈顿距离。

【样例输入】

4 2
2 1
1 4
4 5
5 3

【样例输出】

6

【数据规模】

60%的数据,D<=2; 100%的数据,2<=N<=1000000,D<=4。

题解

对于二维情况,我们发现

dis=|x_1-x_2|+|y_1-y_2|=max(x_1-x_2+y_1-y_2,-x_1+x_2+y_1-y_2,x_1-x_2-y_1+y_2,-x_1+x_2-y_1+y_2)

考虑拆去绝对值,那么两个符号共有4种情况,对于D维,就有2^D种情况,
我们O(n)处理每种情况的最大值与最小值之差,然后取最大值即可

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;

int best[100][5];
int now[5],D,n,ans,dis;
bool first=true;
int main(){
	freopen("jail.in","r",stdin);
	freopen("jail.out","w",stdout);
	scanf("%d%d",&n,&D);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=D;j++)
			scanf("%d",&now[j]);
		if(first){
			first=false;
			for(int k=0;k<(1<<D);k++){
				best[k][0]=0;
				for(int j=1;j<=D;j++){
					best[k][j]=now[j];
					best[k][0]+=(k&(1<<(j-1))?1:-1)*now[j];
				}
			}
		}
		for(int k=0;k<(1<<D);k++){
			dis=0;now[0]=0;
			for(int j=1;j<=D;j++)
				dis+=abs(best[k][j]-now[j]),
				now[0]+=(k&(1<<(j-1))?1:-1)*now[j];
			ans=max(dis,ans);
			if(now[0]>best[k][0]){
				for(int j=0;j<=D;j++)
					best[k][i]=now[i];
			}
		}
	}
	printf("%d",ans);
}

原文地址:https://www.cnblogs.com/Anoxiacxy/p/7183932.html