2017.7 13 NOIP模拟赛

2017.7 13 模拟

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

本人分数:190 (其实120 小小改动了一下才190)

【试题概览】

试题名称序列矩形看守
提交文件sequencerectanglelockjail
输入文件sequence.inrectangle.inlock.injail.in
输出文件sequence.outrectangle.outlock.outjail.out
时间限制1s1s1s1s
空间限制128MB128MB128MB128MB
题目来源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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值