2017.7 13 模拟
全国信息学分区联赛模拟试题(五)
本人分数:190 (其实120 小小改动了一下才190)
【试题概览】
试题名称 | 序列 | 矩形 | 锁 | 看守 |
---|---|---|---|---|
提交文件 | sequence | rectangle | lock | jail |
输入文件 | sequence.in | rectangle.in | lock.in | jail.in |
输出文件 | sequence.out | rectangle.out | lock.out | jail.out |
时间限制 | 1s | 1s | 1s | 1s |
空间限制 | 128MB | 128MB | 128MB | 128MB |
题目来源 | TopCoder |
1.序列
【题目描述】
有一个整数序列,它的每个数各不相同,我们不知道它的长度是多少(即整数个数),但我但我
们知道在某些区间中至少有多少个整数,用区间(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
题解
差分约束 同洛谷的某题 种树
差分约束维护的是一个前缀和
满足下面的式子
s[x]−s[y]>=c
s[i]<=s[i+1]<=s[i]+1
s[start]<=s[i](1<=i<=end)
所以可以差分约束连边
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define inf 0x7ffffff
#define N 10005
using namespace std;
struct edge{int to,next,w;}e[1000001];
int head[N],d[N],cnt,start,flag[N];queue<int>q;
int end=1;
void insert(int x,int y,int z){e[++cnt].to=y;e[cnt].w=z;e[cnt].next=head[x];head[x]=cnt;}
void spfa(){
memset(d,-1,sizeof(d));
q.push(start);
d[start]=0;
while(!q.empty())
{
int k=q.front();q.pop();flag[k]=0;
for(int i=head[k];i;i=e[i].next){
int kk=e[i].to;
if(d[kk]<d[k]+e[i].w){
d[kk]=d[k]+e[i].w;
if(!flag[kk]){
flag[kk]=1;
q.push(kk);
}
}
}
}
}
int main(){
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
int n;int x,y,c;
scanf("%d",&n);
start=0;
for(int i=1;i<=n;i++){
scanf("%d%d%d",&x,&y,&c);
if(c==0) continue;
insert(x-1,y,c);
end=max(end,y);
}
for(int i=1;i<=end;i++){
insert(i-1,i,0);insert(i,i-1,-1);
}
for(int i=1;i<=end;i++)
insert(start,i,0);
spfa();
//printf("%d",end);
printf("%d",d[end]);
return 0;
}
2. 矩形
【题目描述】
给你个 01 矩阵,问共有多少个不同的全 0 矩阵?
【输入文件】
第一行两个整数 N,M;
接下来 N 行,每行 M 个字符,如果字符为‘.’,表示这格可行(即表示数字 0);
如果为’×’,表示不可行(即表示数字 1)。
【输出文件】
一个数表示答案。
【样例输入】
6 4
. . . .
.× × ×
.× . .
.× × ×
. . .×
.× × ×
【样例输出】
38
【数据规模】
30%的数据,N,M<=50;
100%的数据,N,M<=200.
题解
枚举矩形的上界和下界
当上界 下界所夹的区间
有x就把这一个区间打上标记
设两个有x的矩形所夹的矩形长度为d
那么 这中间就会有(d*(d+1))/2个矩形
枚举上下界的时间复杂度为n^2 扫描的复杂度为m
所以是O(n^2m)的复杂度
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int mp[250][250];
int cnt[250];
int ans;
int main(){
freopen("rectangle.in","r",stdin);
freopen("rectangle.out","w",stdout);
int n,m;
char tmp[250];
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",tmp+1);
for(int j=1;j<=m;j++)
{
if(tmp[j]=='*')
mp[i][j]=1;
}
}
for(int i=1;i<=n;i++){//i上界j下界
memset(cnt,0,sizeof(cnt));
for(int j=1;j<=m;j++){
if(mp[i][j]) cnt[j]++;
}
for(int j=i+1;j<=n+1;j++){//更新第j行 实际上是在更新第j-1行 所以到n+1
int pre=0;
for(int k=1;k<=m;k++){
if(!cnt[k]) continue;
int len=k-pre-1;
ans+=len*(len+1)/2;
pre=k;
}
if(pre!=m){
int len=m-pre;
ans+=len*(len+1)/2;
}
for(int k=1;k<=m;k++){
if(mp[j][k]) cnt[k]++;//当前行更新cnt值
}
}
}
printf("%d",ans);
return 0;
}
3 . 锁
【题目描述】
给出 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。
题解
手写n=1,2,3,4的情况
会找到规律(看题解)
每次可以把2^n个数劈成两半
那么 第2^(n-1)+1 个数是第2^(n-1)+ 2^(n-1)得来的
就是二进制的第i位加了1
然后会发现 前面的2^i-1个数的第i位为1
就可以得到第2^(i-1)+1 后面的数
eg:
n=3 有
000 001 011 010 ==|== 110 100 101 111
中间分一半的话 后面的都是前面的第n位为1得来的
graph LR
010-->110
000-->100
001-->101
011-->111
那么根据这个规律就可以找到n为定值时的第k个
比较是 因为前2^n 个最大的是2^n-1 所以进行比较
都是一个递归过程
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define ll long long
using namespace std;
ll a;
ll b;
ll getnum(ll n,ll k){
if(n==0) return 0;
ll half=(1ll<<(n-1));
if(k<=half) return getnum(n-1,k);
else if(k==half+1) return getnum(n-1,half)+half;
else return getnum(n-1,k-half-1)+half;
}
ll getmax(ll n,ll k){
if(n==0) return 0;
ll half=(1ll<<(n-1));
if(k<=half) return getmax(n-1,k);
else
{
ll tmp=getnum(n,half+1);
if(k==half+1) return tmp;
else return max(tmp,getmax(n-1,k-half-1)+half);
}
}
int out[15000];
int main(){
freopen("lock.in","r",stdin);
freopen("lock.out","w",stdout);
scanf("%lld%lld",&a,&b);
ll ans=getmax(a,b); //printf("%lld\n",ans);
// printf("%lld\n",ans);
for(int i=a-1;i>=0;i--){
cout<<(((1ll<<i)&ans)?"1":"0");
}
return 0;
}
4 、 看守
【题目描述】
给出 D 维空间的 N 个点,求曼哈顿距离最大的两个点的曼哈顿距离。两个 D 维
的点(x1,x2,…xD),(y1,y2,…yD)的曼哈顿距离定义为
\sum_i |xi-yi|
【输入文件】
第一行两个整数 N,D;
接下来 N 行,每行 D 个整数描述一个点的坐标。
【输出文件】
输出最大的曼哈顿距离。
【样例输入】
4 2
2 1
1 4
4 5
5 3
【样例输出】
6
【数据规模】
60%的数据,D<=2;
100%的数据,2<=N<=1000000,D<=4。
题解
自己先写出二维的情况
去掉绝对值 变成(?xi ?yi)-(?xj ?yj)
‘?’指不清它的符号
此时枚举这个符号就行了
最多4维 所以最大为2^4
可以把1看为正号 0看为符号
此时是一个d位的二进制数
从1枚举到2^d 把每个二进制数代入
求一个最小值 一个最大值
每一个代入得到的答案就是最大减去最小
所以在每个得到的答案中选一个最大的就行了
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define inf 0x7ffffff
using namespace std;
int maxn=-inf,minn=inf;
int n,d;
int a[1000005][5];
void solve(int x){
for(int i=1;i<=n;i++){
int tot=0;
for(int j=1;j<=d;j++){
if((1<<j)&x)
tot+=a[i][j];
else tot-=a[i][j];
}
maxn=max(tot,maxn);
minn=min(tot,minn);
}
}
int main(){
freopen("jail.in","r",stdin);
freopen("jail.out","w",stdout);
int ans=-inf;
scanf("%d%d",&n,&d);
for(int i=1;i<=n;i++)
for(int j=1;j<=d;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=(1<<d);i++){
maxn=-inf;minn=inf;
solve(i);
ans=max(ans,maxn-minn);
}
printf("%d",ans);
return 0;
}