2017.7 13 NOIP模拟赛

来源:互联网 发布:内存卡数据恢复软件 编辑:程序博客网 时间:2024/06/06 17:21

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 10005using 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 LR010-->110000-->100001-->101011-->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 0x7ffffffusing 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;}