NOIP摩尼赛(或许普及难度?)

考前日常。。。

1、某种密码(password.*)

    关于某种密码有如下描述:某种密码的原文A是由N个数字组成,而密文B是一个长度为N的01数串,原文和密文的关联在于一个钥匙码KEY。若KEY=∑▒〖Ai*Bi〗,则密文就是原文的一组合法密码。

         现在有原文和钥匙码,请编一个程序来帮助他统计到底有多少个符合条件的密文。

 

【输入数据】

         第一行两个数N,KEY,意义同题目描述;

         第二行N个数表示原文A,意义同题目描述。

 

【输出数据】

         一个数ANS,表示对于原文A和KEY,有多少组可行的密文B。

 

【输入样例】

3 2

1 1 2

 

【输出样例】

2

 

【样例说明】

密文110,1*1+1*1+0*2=2

密文001,0*1+0*1+1*2=2

一共两组可行的密文。

 

【数据约定】

60%数据满足N<=25

100%数据满足N<=40,-maxlongint<=∑▒Ai<=maxlongint


 

初看觉水;

思路历程:搜索(2^n ???)----》背包(maxint???)---》背包+hash(n的大小不允许我这样做)---》折半搜索+hash桶计(海星)

没什么说的了,下面是CPP

#include<bits/stdc++.h>
using namespace std;
//long long bao1[1200000],bao2[1200000];
int n,N;
long long a[50],key,ji1=0;
map<long long,int>hash;
inline void dfs1(int pos,long long sum)
{
//    if(sum>key)return ;
    if(pos==N)
    {
//        bao1[++ji1]=sum;
//        bao1[++ji2]=sum+a[pos];
        hash[sum]++;hash[sum+a[pos]]++;
        return ;
    }
    dfs1(pos+1,sum+a[pos]);
    dfs1(pos+1,sum);
} 
inline void dfs2(int pos,long long sum)
{
//    if(sum>key)return ;
    if(pos==n)
    {
//        bao2[++ji2]=sum;
//        bao2[++ji2]=sum+a[pos];
        ji1+=hash[key-sum];ji1+=hash[key-sum-a[pos]];
        return ;
    }
    dfs2(pos+1,sum+a[pos]);
    dfs2(pos+1,sum);
}

int main()
{
//    freopen("DDD.in","r",stdin);
    scanf("%d%lld",&n,&key);
    for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
//    sort(a+1,a+n+1);
    N=n/2;
    dfs1(1,0);
    dfs2(N+1,0);
//    int ji1=(1<<N),ji2=(1<<(n-N-1));
//    for(int i=1;i<=ji1;++i)
//    {
//        map[bao1[i]]++;
//    } 
//    sort(bao1+1,bao1+ji1+1);
//    sort(bao2+1,bao2+ji2+1,PP);
//    int j=ji2,ans=0;
//    for(int i=1;i<=ji1;++i)
//    {
//        bool fla=0;
//        while(bao1[i]+bao2[j]>key)
//        {
//            j--;
//        }
//        while(bao1[i]+bao2[j]==key)
//        {
//            ans++;i++;j--;fla=1;
//        }
//        if(fla)i--;
//    }
    printf("%lld",ji1);
    return 0;
}
/*
40 5
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
*/

注释的地方有一些剪枝,当然加了会有红紫;


 

2、球的序列(formation.*)

   N个编号为1-n的球,每个球都有唯一的编号。这些球被排成两种序列,分别为A、B序列,现在需要重新寻找一个球的序列l,对于这个子序列l中任意的两个球,要求j,k(j<k),都要求满足lj在A中位置比lk在A中位置靠前,且lj在B中位置比lk在B中位置靠前,请你计算这个子序列l的最大长度。

输入:

第一行一个整数,表示N。

第二行N个整数,表示A序列。

第三行N个整数,表示B序列。

 

样例输入

5

1 2 4 3 5

5 2 3 4 1

 

样例输出

2

样例说明

L可以是{2,3},也可以是{2,4}

 

数据范围:

40% N<=5000

100% N<=50000


 

依旧是水的;

分吸分吸:如果对 Lj,Lk( j<k ) 有  ALj  在 ALk  前,且B Lj  在 B Lk 前;那么Lj,Lk 在A,B中相对位置不变;即LCS;

nlogn求:

#include<bits/stdc++.h>
using namespace std;
int read()
{
    int x,f=0;char ch=getchar();
    while(!isdigit(ch)){f=1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return f?-x:x; 
}
int f[50100],A[50100],B[50100],ma[50100],mb[50100],len,n;
int che(int x)
{
    int l=0,r=len+1,mid;
    while(l<r)
    {
        mid=((l+r)>>1);
        if(f[mid]>=x)
        {
            r=mid;
        }
        else
        {
            l=mid+1;
        }
    }
    return l;
}
int main()
{
//    n=read();
//    for(int i=1;i<=n;++i)
//    {    A[i]=read();    ma[A[i]]=i;    }
//    for(int i=1;i<=n;++i)
//    {    B[i]=read();    mb[i]=ma[B[i]];    }
    freopen("formation.in","r",stdin);
    freopen("formation.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&A[i]);ma[A[i]]=i;
    }
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&B[i]);mb[i]=ma[B[i]];
    }
    f[1]=mb[1];
    len=1;
    for(int i=2;i<=n;++i)
    {
        if(mb[i]>f[len])
        {
            len++;f[len]=mb[i];
        }
        else
        {
            int pos=che(mb[i]);
            f[pos]=mb[i];
        }
    }
    printf("%d",len);
    return 0;
 } 

3大逃亡(escape.*)

给出数字N(1<=N<=10000),X(1<=x<=1000),Y(1<=Y<=1000),代表有N个敌人分布一个X行Y列的矩阵上,矩形的行号从0到X-1,列号从0到Y-1再给出四个数字x1,y1,x2,y2,代表你要从点(x1,y1)移到(x2,y2)。在移动的过程中你当然希望离敌人的距离的最小值最大化,现在请求出这个值最大可以为多少,以及在这个前提下,你最少要走多少步才可以回到目标点。注意这里距离的定义为两点的曼哈顿距离,即某两个点的坐标分为(a,b),(c,d),那么它们的距离为|a-c|+|b-d|。

输入:

第一行给出数字N,X,Y

第二行给出x1,y1,x2,y2

下面将有N行,给出N个敌人所在的坐标

输出:

在一行内输出你离敌人的距离及在这个距离的限制下,你回到目标点最少要移动多少步。

Sample input

2 5 6

0 0 4 0

2 1

2 3

Sample output

2 14


题意在明示二分与敌人最小距离......;

1st:floodfill(BFS)处理每个点与最近敌人的距离;

2nd:二分与敌人最小距离再BFS寻终点;

时间:O(log(n+m)*(n+m)+n+m)(当然是口胡了)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
bool mark[1010][1010];
int map[1010][1010];
int x[5000010],y[5000010];
int bs[1010][1010];
int n,m,k,tou,wei,qx,qy,zx,zy;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
void init()
{
    int i,a,b;
    scanf("%d%d%d",&k,&n,&m);
    tou=1;wei=0;
    scanf("%d%d%d%d",&qx,&qy,&zx,&zy);
    qx++;qy++;zx++;zy++;
    for(i=1;i<=k;i++)
    {
        scanf("%d%d",&a,&b);
        a++;b++;
        wei++;x[wei]=a;y[wei]=b;
        mark[a][b]=1;
    }
}
void bfs()
{
    int i,nx,ny,xx,yy,p;
    while(tou<=wei)
    {
        xx=x[tou];yy=y[tou];tou++;
        for(p=0;p<4;p++)
        {
            nx=xx+dx[p];ny=yy+dy[p];
            if(nx<1 || nx>n || ny<1 || ny>m)   continue;
            if(!mark[nx][ny])
            {
                mark[nx][ny]=1;map[nx][ny]=map[xx][yy]+1;
                wei++;x[wei]=nx;y[wei]=ny;
            }
        }    
    }
}
bool check(int mid)
{
    int i,xx,yy,nx,ny,p;
    tou=1;wei=1;x[1]=qx;y[1]=qy;
    memset(mark,0,sizeof(mark));
    memset(bs,127,sizeof(bs));
    mark[qx][qy]=1;bs[qx][qy]=0;
    while(tou<=wei)
    {
        xx=x[tou];yy=y[tou];tou++;
        for(p=0;p<4;p++)
        {
            nx=xx+dx[p];ny=yy+dy[p];
            if(nx<1 || nx>n || ny<1 || ny>m || map[nx][ny]<mid)   continue;
            if(!mark[nx][ny])
            {
                mark[nx][ny]=1;bs[nx][ny]=bs[xx][yy]+1;
                wei++;x[wei]=nx;y[wei]=ny;
            }
        }
    }
    if(mark[zx][zy])  return 1;
    else return 0;
}
int main()
{
    freopen("escape.in","r",stdin);
    freopen("escape.out","w",stdout);
    init();
    bfs();
    int l=0,r=map[qx][qy],mid;
    while(l+1<r)
    {
        mid=(l+r)>>1;
        if(check(mid))  l=mid;
        else r=mid;
    }
    if(check(r)) 
    {cout<<r<<" "<<bs[zx][zy]<<endl;}
    else
    {check(l);cout<<l<<" "<<bs[zx][zy]<<endl;}
//    system("pause");
    return 0;
}

如果真爱有颜色,那么一定是红橙黄紫深蓝吧;

 

 

posted @ 2018-10-04 23:07  sqrt(3B)  阅读(95)  评论(0编辑  收藏  举报