全国信息学分区联赛模拟试题(五)
【试题概览】
试题名称 | 序列 | 矩形 | 锁 | 看守 |
---|---|---|---|---|
提交文件 | 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);
}