2014-11-3 NOIP模拟赛3

字符串

string.pas/c/cpp

1S/256MB

【题目描述】

       现在给一个字符串,你要做的就是当这个字符串中存在两个挨着的字符是相同的时就将这两个字符消除。需要注意的是,当把这两个字符消除后,可能又产生一对新的挨着的字符是相同的。比如,初始的字符串是abcddc,dd是两个挨着的相同的字符,当把"dd"消除后,得到的字符串是abcc,这时cc又是两个挨着的相同的字符,所以又应该把cc消除。重复以上操作直到剩下的串中不存在两个挨着的字符是相同的为止,输出最终剩下的串。另外需要注意的是,多对相同字符的消除顺序是不会对答案产生影响的,可以证明最后他们都会达到唯一的结果,比如,对于初始字符串adccdeed,无论是adccdeed->addeed->aeed->ad还是adccdeed->adccdd->adcc->ad,最终的输出结果都是ad。

【输入】

输入文件名:string.in

输入的第一行,包含一个字符串,为初始字符串,所有的字符均为小写字母。

【输出】

输出文件名:string.out

输出为一行,包含一个字符串,为执行多次消除操作后最终剩下的字符串。

【输入样例】

       adccdeed

【输出样例】

ad

【数据范围】

       对于100%的数据,字符串的长度在1到200000之间。

/*
    操作很麻烦,写了一个大模拟,用双向链表维护删除操作,用时1h,总觉得有超时的风险 
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 200010
using namespace std;
int a[maxn],fir=1,len,now;
bool vis[maxn];
struct node{
    int pre,to;
}q[maxn];
char s[maxn];
bool check(){
    while(vis[fir]==1)fir++;
    int before=0;
    now=fir;
    for(;now<=len;now=q[now].to){
        if(a[now]==a[before])return 0;
        before=now;
    }
    return 1;
}
int main(){
    //freopen("Cola.txt","r",stdin);
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    scanf("%s",s+1);
    len=strlen(s+1);
    for(int i=1;i<=len;i++){
        a[i]=s[i]-'a';
        q[i].pre=i-1;q[i].to=i+1;
    }
    a[0]=-1;
    while(1){
        if(check()){
            while(vis[fir]==1)fir++;
            while(fir<=len){
                printf("%c",a[fir]+'a');
                fir=q[fir].to;
            }
            break;
        }
        while(vis[fir]==1)fir++;
        int before=0;
        now=fir;
        int flag=0,cnt=1,head=fir,headto;
        for(;now<=len;now=q[now].to){
            if(a[now]==a[before]){
                if(flag==0)head=before,headto=now;
                flag=1;
                vis[now]=1;vis[before]=1,cnt++;
                q[q[now].to].pre=q[before].pre;
                q[now].pre=q[before].pre;
                q[q[before].pre].to=q[now].to;
                q[before].to=q[now].to;
                break;
            }
            before=now;
        }
    }
}
60分 双向链表+模拟 TLE
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define pa pair<int,int>
#define inf (1LL<<60)
#define mod 1000000007
#define ll long long
using namespace std;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
char a[200005];
int n;
int l[200005],r[200005];
void del(int x){
    int L=l[x],R=r[x];
    r[L]=R;l[R]=L;
}
int main(){
    //freopen("string.in","r",stdin);
    //freopen("string.out","w",stdout);
    scanf("%s",a+1);n=strlen(a+1);
    for(int i=0;i<n;i++)r[i]=i+1;
    for(int i=1;i<=n;i++)l[i]=i-1;
    for(int now=r[0];now;now=r[now]){
        while(now&&a[now]==a[l[now]]){
            int t=r[now];
            del(l[now]);del(now);
            now=t;
        }
    }
    for(int now=r[0];now;now=r[now])
        printf("%c",a[now]);
    puts("");
    return 0;
}
100分 栈+链表模拟

 

 

 

 

感冒病毒

suspects.pas/c/cpp

1S/256MB

【题目描述】

       一种感冒病毒正在学校里传播,这所学校有n个学生,m个学生社团,每个学生可能参加了多个社团,因为同一个社团的学生交流较多,所以如果一个学生感染上感冒病毒,那么他所在的社团里的所有学生都会感染上感冒病毒,现在已知0号学生感染上感冒病毒,问现在有多少人会感染上感冒病毒。

【输入】

输入文件:suspects.in

输入的第一行是两个整数n和m,表示学生的数目和社团的数目,学生的编号为0到n-1。

接下来m行,每行首先是一个数ki,表示这个社团有ki个人,接下来ki个整数,表示这个社团里每个学生的编号aij。

【输出】

输出文件:suspects.out

输出为一行,包含一个整数。表示感染感冒病毒的人数。

【输入样例】

100 4

2 1 10

5 10 13 11 12 14

2 0 1

2 9 2

【输出样例】

7

【数据范围】

       对于100%的数据,3<=n<=30000

     对于100%的数据,3<=m<=500

对于100%的数据,1<=ki<=n

       对于100%的数据,0<=aij<n。

/*
    一眼题,一看就是并查集,就写出来了,用时10分钟 
*/
#include<iostream>
#include<cstdio>
#define maxn 30010
using namespace std;
int n,m,fa[maxn];
int find(int x){
    if(fa[x]==x)return fa[x];
    return fa[x]=find(fa[x]);
}
void connect(int a,int b){
    int f1=find(a);
    int f2=find(b);
    if(f1==f2)return;
    fa[f1]=f2;
}
int main(){
    freopen("suspects.in","r",stdin);
    freopen("suspects.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)fa[i]=i;
    int x,y,z;
    for(int i=1;i<=m;i++){
        scanf("%d",&x);
        if(x>0)scanf("%d",&y),y++;
        if(x>1){
            for(int j=2;j<=x;j++){
                scanf("%d",&z);z++;
                connect(y,z);
            }
        }
    }
    int ans=1;
    for(int i=2;i<=n;i++)
        if(find(i)==find(1))ans++;
    printf("%d",ans);
}
100分 并查集

 

 

 

 

弱点

weakness.pas/c/cpp

2S/256MB

【题目描述】

       一队勇士正在向你进攻,每名勇士都有一个战斗值ai。但是这队勇士却有一个致命弱点,如果存在i<j<k使得ai>aj>ak,则会影响他们整体的战斗力。我们将这样的一组(i,j,k)称为这队勇士的一个弱点。请求出这队勇士的弱点数目。

【输入】

输入文件:weakness.in

输入的第一行是一个整数n,表示勇士的数目。

接下来一行包括n个整数,表示每个勇士的战斗值ai。

【输出】

输入文件:weakness.out

输出为一行,包含一个整数。表示这队勇士的弱点数目。

【输入样例】

4

10 8 3 1

【输出样例】

4

【数据范围】

       对于30%的数据,3<=n<=100

     对于100%的数据,3<=n<=1000000

       对于100%的数据,1<=ai<=1000000,每个ai均不相同

#include<iostream>
#include<cstdio>
using namespace std;
#define maxn 1000010
int n,a[maxn],ans;
int main(){
    freopen("weakness.in","r",stdin);
    freopen("weakness.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            for(int k=j+1;k<=n;k++){
                if(a[i]>a[j]&&a[j]>a[k])ans++;
            }
        }
    }
    cout<<ans;
    return 0;
}
30分 暴力
/*
    枚举i的话,要求左边比它大的个数和右边比它小的
*/
#include<iostream>
#include<cstdio>
using namespace std;
#define maxn 1000001
int n,tr[2][maxn],a[maxn];
long long query(int x,int pos){
    long long res=0;
    while(pos){
        res+=tr[x][pos];
        pos-=pos&(-pos);
    }
    return res;
}
void add(int x,int pos,int d){
    while(pos<=maxn){
        tr[x][pos]+=d;
        pos+=pos&(-pos);
    }
}
int main(){
    freopen("weakness.in","r",stdin);
    freopen("weakness.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        add(0,a[i],1);
    }
    long long ans=0;
    for(int i=1;i<=n;i++){
        add(0,a[i],-1);
        ans+=(query(1,1000000)-query(1,a[i]))*(query(0,a[i]-1));
        add(1,a[i],1);
    }
    cout<<ans;
}
100分 树状数组

 

 

 

滑动的窗户

window.pas/c/cpp

3S/256MB

【题目描述】

       在一个包含n个元素的数组上,有一个长度为k的窗户在从左向右滑动。窗户每滑动到一个位置,我们都可以看到k个元素在窗户中。如下的例子所示,假设数组为 [1 3 -1 -3 5 3 6 7],而k等于3:

 

窗户位置

最小值

最大值

[1  3  -1] -3  5  3  6  7

-1

3

1 [3  -1  -3] 5  3  6  7

-3

3

1  3 [-1  -3  5] 3  6  7

-3

5

1  3  -1 [-3  5  3] 6  7

-3

5

1  3  -1  -3 [5  3  6] 7

3

6

1  3  -1  -3  5 [3  6  7]

3

7

 

       对于窗户滑动过的每个位置,请给出窗户内k个元素的最小值和最大值。

【输入】

输入文件:window.in

输入的第一行包括两个整数n,k,n表示数组的长度,k表示窗户的长度。

接下来一行包括n个整数,表示这个n个元素的数组。

【输出】

输出文件:window.out

输出包含两行,每行包括n-k+1个整数,第一行表示窗户从左到右滑动过程中的最小值,第二行表示窗户从左到右滑动过程中的最大值。

【输入样例】

8 3

1 3 -1 -3 5 3 6 7

【输出样例】

-1 -3 -3 -3 3 3

3 3 5 5 6 7

【数据范围】

     对于100%的数据,3<=n<=1000000,1<=k<=n,数组中的每个元素均在int范围内

#include<iostream>
#include<cstdio>
using namespace std;
#define maxn 1000010
int n,k,qmax[maxn],qmin[maxn],a[maxn],h1=1,h2=1,t1,t2;
int ans1[maxn],ans2[maxn];
int main(){
    //freopen("Cola.txt","r",stdin);
    freopen("window.in","r",stdin);
    freopen("window.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<k;i++){
        while(h1<=t1&&a[qmax[t1]]<=a[i])t1--;
        qmax[++t1]=i;
        while(h2<=t2&&a[qmin[t2]]>=a[i])t2--;
        qmin[++t2]=i;
    }
    for(int i=k;i<=n;i++){
        while(h1<=t1&&qmax[h1]<=i-k)h1++;
        while(h1<=t1&&a[qmax[t1]]<=a[i])t1--;
        qmax[++t1]=i;ans1[i]=a[qmax[h1]];
        while(h2<=t2&&qmin[h2]<=i-k)h2++;
        while(h2<=t2&&a[qmin[t2]]>=a[i])t2--;
        qmin[++t2]=i;ans2[i]=a[qmin[h2]];
    }
    for(int i=k;i<=n;i++)printf("%d ",ans2[i]);printf("\n");
    for(int i=k;i<=n;i++)printf("%d ",ans1[i]);
    fclose(stdin);fclose(stdout);
    return 0;
}
100分 单调队列

 

 

 

posted @ 2017-09-27 10:29  Echo宝贝儿  阅读(308)  评论(0编辑  收藏  举报