[noip模拟赛2017.7.13]

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

【试题概览】

试题名称序列矩形看守
提交文件sequencerectanglelockjail
输入文件sequence.inrectangle.inlock.injail.in
输出文件sequence.outrectangle.outlock.outjail.out 时间限制
空间限制128MB128MB128MB128MB

题目来源 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值