Test 5 for NOIP

来源:互联网 发布:mac pro 关闭手写输入 编辑:程序博客网 时间:2024/05/20 02:21

头大

这个暑假完就要去搞NOIP了。。。

暑假55天也就20次测试。。。为防万一我还是每次测试玩都写个总结。。


课程

鉴于现在就开始模拟noip一考就两天我们吃不消于是变成了隔天考。。八月份回来再实行两天一考。


论T1 RMQ被卡(标答是单调队列) T2数位dp不会 T3线段树不会的尴尬处境。。。

Test 5(0/300)

T1 滑动的窗户

题目描述
在一个包含 n 个元素的数组上,有一个长度为 k 的窗户在从左向右滑动。窗户每滑动到一个位置,我们都可以看到 k 个元素在窗户中。如下的例子所示,假设数组为 [1 3 -1 -3 5 3 6 7],而 k 等于 3 :
这里写图片描述
对于窗户滑动过的每个位置,请给出窗户内 k 个元素的最小值和最大值。

输入格式
输入的第一行包括两个整数 n,k ,n 表示数组的长度,k 表示窗户的长度。
接下来一行包括 n 个整数,表示这个 n 个元素的数组。

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

样例数据 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 范围内。

MY.CPP

#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<ctime>#include<cctype>#include<cstring>#include<string>#include<algorithm>using namespace std;int l,r;int n,k,num[1000500];int rmq[1000500][23];int rmx[1000500][23];int jud(int a,int b){    if(a>=0&&b>=0)  return min(a,b);    else if(a>=0&&b<0)  return b;    else if(a<0&&b>=0)  return a;    else    return -max(-a,-b);}int check(){    for(int j=0;(1<<j)<=n;j++)    {           for(int i=1;i+(1<<j)-1<=n;i++)        {            cout<<"rmx["<<i<<"]["<<j<<"] = "<<rmx[i][j]<<endl;        }        cout << endl;    }    cout << endl;}void init(){    for(int i=1;i<=n;i++)rmq[i][0] = rmx[i][0] = num[i];    for(int j=1;(1<<j)<=n;j++)    {           for(int i=1;i+(1<<j)-1<=n;i++)        {            rmq[i][j] = max(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);            rmx[i][j] = min(rmx[i][j-1],rmx[i+(1<<(j-1))][j-1]);        }    }}int main(){    freopen("window.in","r",stdin);    freopen("window.out","w",stdout);    cin >> n >> k;    for(int i=1;i<=n;i++)        cin >> num[i];    init();    l=0,r=k-1;    int ky = (int)(log(k)/log(2));    for(int i=1;i<=n-k+1;i++)    {        l+=1;   r+=1;        cout << min(rmx[l][ky],rmx[r-(1<<ky)+1][ky]) << " ";    }    l=0,r=k-1;  cout << endl;    for(int i=1;i<=n-k+1;i++)    {        l+=1;   r+=1;        cout << max(rmq[l][ky],rmq[r-(1<<ky)+1][ky]) << " ";    }    return 0;}

所以说比赛时还要注意时间限制,空间限制。。贸然用RMQ的后果就是被心狠的出题人摆了一套全部ME。。
标算是单调队列。RMQ(N*logN),单调(N)

STD.CPP

#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<ctime>#include<cctype>#include<cstring>#include<string>#include<algorithm>#include<vector>using namespace std;int n,k,head,tail;int sta[1000050];int pos[1000050];inline int read(){    int data=0,w=1; char ch=0;    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();    if(ch=='-') w=-1,ch=getchar();    while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();    return data*w;}inline void write(int x){     if(x<0) putchar('-'),x=-x;     if(x>9) write(x/10);     putchar(x%10+'0');}int main(){    n=read();   k=read();    for(int i=1;i<=n;i++)        sta[i]=read();    head = tail = 0;    for(int i=1;i<=n;i++)    {        while(head<tail&&pos[head]<=i-k)++head;        while(head<tail&&sta[pos[tail-1]]>=sta[i])--tail;        pos[tail] = i;  tail += 1;        if(i>=k)    write(sta[pos[head]]),cout << " ";    }    cout << endl;    head = tail = 0;    for(int i=1;i<=n;i++)    {        while(head<tail&&pos[head]<=i-k)++head;        while(head<tail&&sta[pos[tail-1]]<=sta[i])--tail;        pos[tail] = i;  tail += 1;        if(i>=k)    write(sta[pos[head]]),cout << " ";    }}

死的只有那么服气。

T2 准考证号(0/100)

题目描述
CLC NOIP2015 惨跪,他依稀记得他的准考证号是 37(其实是假的),现在CLC又将要面临一场比赛,他希望准考证号不出现 37(连续),同时他又十分讨厌 4 ,所以也不希望 4 出现在准考证号中。现在他想知道在 A 和 B 之间有多少合法的准考证号

输入格式
输入包含两个整数,A B。

输出格式
输出一个整数。

样例数据 1
输入  [复制]

1 10
输出

9
样例数据 2
输入  [复制]

25 50
输出

14
备注
【数据规模和约定】
20% 的数据,满足:1<=A<=B<=1000000 。
100% 的数据,满足:1<=A<=B<=2000000000 。

MY.CPP

#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<ctime>#include<cctype>#include<cstring>#include<string>#include<algorithm>using namespace std;long long a,b,a1,b1;int cnts=0,cntt=0;int s[15],t[15];int f[10][10],g4[15];int get(long long num,int jud){    int res=0;    while(num)      {        if(!jud)    s[++cnts]=num%10;        else        s[++cntt]=num%10;        res+=1; num/=10;    }    return res;}void initi(){    int res = 0,jud = 1;    for(int i=1;i<=10;i++)    {        jud = jud*i;        res = res+jud;        g4[i] = res;    }}int get4(int num,int cnt){    if(num<4)   cnt-=1;    return g4[cnt];}   int g37[10] = {0,0,1,2,4,7,12,20,32,50};int get37(int num,int cnt){    if(num<4)   cnt-=1;    return g37[cnt];}int main(){    freopen("ticket.in","r",stdin);    freopen("ticket.out","w",stdout);    cin >> a >> b;    initi();    a1 = get(a,0);  b1 = get(b,1);    int ans1=0,ans2=0;    for(int i=b1;i>=1;i--)    {        ans1 += get4(s[i],i) + get37(s[i],i);    }    for(int i=a1;i>=1;i--) ans2 += get4(s[i],i) + get37(s[i],i);    cout << ans1-ans2 << endl;    return 0;}

一通乱搞想搞出数位dp。。。但这乱搞得八竿子都打不着也是可以。。
答案就是数位dp,而且几乎是裸题。(不要62)

STD.CPP

#include<iostream>#include<iomanip>#include<cstdio>#include<cstdlib>#include<string>#include<cstring>#include<algorithm>#include<cmath>using namespace std;long long n,m;int a[10],dp[10][3];long long calc(long long x){    long long sum = x;    long long ans = 0;    int num = 0;    bool flag = false;    while(x)    {        a[++num] = x%10;        x/=10;    }    a[num+1] = 0;    for(int i=num;i>=1;i--)    {        ans += dp[i-1][2]*a[i];        if(flag)    ans += dp[i-1][0]*a[i];        else        {            if(a[i]>4)  ans += dp[i-1][0];            if(a[i+1]==3&&a[i]>7)   ans += dp[i][1];            if(a[i]>3)  ans += dp[i-1][1];            if(a[i]==4||(a[i+1]==3&&a[i]==7))   flag = true;        }    }    if(flag)    ans++;    return sum-ans;}void initi(){    memset(dp,0,sizeof(dp));    dp[0][0] = 1;    for(int i=1;i<=9;i++)    {        dp[i][0] = dp[i-1][0]*9 - dp[i-1][1];        dp[i][1] = dp[i-1][0];        dp[i][2] = dp[i-1][0] + dp[i-1][1] + dp[i-1][2]*10;     }}int main(){    initi();    cin >> n >> m;    cout << calc(m) - calc(n-1) << endl;}

T3 Query(0/100)

题目描述
万恶的大头又出现了!他正在玩一个智障游戏:打怪兽。

现在大头的屏幕上出现了一排怪兽,每只怪兽头上有一个血条,每次大头可以选择一个区间进行攻击,攻击值为 K ,这个区间中血量小于 K 的怪兽都会被大头无情地干掉,当然怪兽不会坐以待毙,对于一个区间的怪兽,他们会在某个时刻血量同时加 X 。

大头头虽然很大,但是 IQ 并不高,在座的各位选手都不知道比他高到哪里去了。这个时候大头使出了大招——作弊器,然而大头的作弊器并不高级只能将选择的区间内血量为 7 的倍数的怪兽干掉,问:他能干掉多少怪兽?

输入格式
第一行一个正整数 n ;
接下来 n 行 n 个整数;
再接下来一个正整数 Q ,表示操作的个数;
接下来 Q 行每行若干个整数。如果第一个数是 add ,后接 3 个正整数 a,b,X,表示在区间 [a,b] 内每个数增加 X,如果是 count,表示统计区间 [a,b] 能被 7 整除的个数。

输出格式
对于每个询问输出一行一个答案。

样例数据 1
输入  [复制]

3
2 3 4
6
count 1 3
count 1 2
add 1 3 2
count 1 3
add 1 3 3
count 1 3
输出

0
0
0
1

MY.CPP

#include<iostream>#include<cstdio>#include<cstdlib>#include<cmath>#include<ctime>#include<cctype>#include<cstring>#include<string>#include<algorithm>using namespace std;int n,q,cnt,mos[100050],pie[320][10],pos[20];int a,b,x;char c[10];void add(){    cin >> a >> b >> x;    int l = a/cnt+1;    int r = b/cnt-1;    if(l<=r)    {        for(int i=a;i<=l*cnt;i++)        {            pie[l-1][mos[i]]-=1;            mos[i] = (mos[i]+x)%7;            pie[l-1][mos[i]]+=1;        }        for(int i=l+1;i<=r;i++)        {            for(int j=0;j<=6;j++)                pos[j+x] = pie[i][j];            for(int j=0;j<=6;j++)                pie[i][(j+x)%7] = pos[j+x];            for(int j=1;j<=cnt;j++)              mos[i*cnt+j] = (mos[i*cnt+j]+x)%7;        }        for(int i=(r+1)*cnt+1;i<=b;i++)        {            pie[r][mos[i]]-=1;            mos[i] = (mos[i]+x)%7;            pie[r][mos[i]]+=1;        }    }    else if(l>r)    {        if(l==r+1)        {            for(int i=a;i<=l*cnt;i++)            {                pie[l-1][mos[i]]-=1;                mos[i] = (mos[i]+x)%7;                pie[l-1][mos[i]]+=1;            }            for(int i=(r+1)*cnt+1;i<=b;i++)            {                pie[r][mos[i]]-=1;                mos[i] = (mos[i]+x)%7;                pie[r][mos[i]]+=1;            }        }        else        {            for(int i=a;i<=b;i++)            {                pie[r+1][mos[i]]-=1;                mos[i] = (mos[i]+x)%7;                pie[r+1][mos[i]]+=1;            }        }    }}void count(){    int ans = 0;    cin >> a >> b ;    int l = a/cnt+1;    int r = b/cnt-1;    if(l<=r)    {        for(int i=a;i<=l*cnt;i++)            if(mos[i]==0)   ans += 1;        for(int i=l;i<=r;i++)            ans += pie[i][0];        for(int i=r*cnt+1;i<=b;i++)            if(mos[i]==0)   ans += 1;    }    else if(l>r)    {        if(l==r+1)        {            for(int i=a;i<=l*cnt;i++)                if(mos[i]==0)   ans += 1;            for(int i=(r+1)*cnt+1;i<=b;i++)                if(mos[i]==0)   ans += 1;           }        else        {            for(int i=a;i<=b;i++)                if(mos[i]==0)   ans += 1;           }    }    cout << ans << endl;}int main(){    freopen("seg.in","r",stdin);    freopen("seg.out","w",stdout);    cin >> n;    cnt = int(sqrt(n));    for(int i=1;i<=n;i++)    {        cin >> mos[i];        mos[i] = mos[i]%7;        pie[i/cnt+1][mos[i]]+=1;    }    cin >> q;    while(q--)    {        cin >> c;        if(c[0]=='a')   add();        else          count();      }    return 0;}

考试时看到区间就想到了之前讲过的分块(小伙子有想法),但是只有理论又没有打过代码那只有GG一条路可走。。。分块事后证明只会有几个点TE。
标答是线段树。。而且是和模板出入很大的那种。

STD.CPP

//std ans of day* t3 线段树 #include<iostream>#include<algorithm>#include<cmath>#include<ctime>#include<cctype>#include<cstdio>#include<queue>#include<cstring>#include<map>using namespace std;const int Maxn=1e5+50;char ch[10];int a[Maxn],sum[Maxn<<2][8],tag[Maxn<<2];int S[8];inline int read(){    char ch=getchar();    int i=0,f=1;    while(!isdigit(ch))    {        if(ch=='-')f=-1;        ch=getchar();    }    while(isdigit(ch))    {        i=(i<<1)+(i<<3)+ch-'0';        ch=getchar();    }    return i*f;}inline void update(int now){    for(int i=0;i<7;++i)        sum[now][i]=sum[now<<1][i]+sum[now<<1|1][i];}inline void build(int k,int l,int r){    if(l==r)    {        ++sum[k][a[l]];        return;    }    int mid=(l+r)>>1;    build(k<<1,l,mid);    build(k<<1|1,mid+1,r);    update(k);}inline void addtag(int k,int v){    tag[k]=(tag[k]+v)%7;    for(int i=0;i<7;++i)    {        int t=(i+v)%7;        S[t]=sum[k][i];    }    for(int i=0;i<7;++i)        sum[k][i]=S[i];}inline void pushdown(int k){    addtag(k<<1,tag[k]);    addtag(k<<1|1,tag[k]);    tag[k]=0;}inline int query(int k,int l,int r,int L,int R){    if(l>=L&&r<=R)        return sum[k][0];    if(tag[k])        pushdown(k);    int mid=(l+r)>>1;    if(R<=mid)        return query(k<<1,l,mid,L,R);    else         if(L>mid)            return query(k<<1|1,mid+1,r,L,R);    else         return query(k<<1,l,mid,L,R)+query(k<<1|1,mid+1,r,L,R);}inline void modify(int k,int l,int r,int L,int R,int v){    if(l>=L&&r<=R)    {        addtag(k,v);        return;    }    if(tag[k])        pushdown(k);    int mid=(l+r)>>1;    if(R<=mid)        modify(k<<1,l,mid,L,R,v);    else         if(L>mid)            modify(k<<1|1,mid+1,r,L,R,v);    else    {        modify(k<<1,l,mid,L,R,v);        modify(k<<1|1,mid+1,r,L,R,v);    }    update(k);}int main(){    int n=read();    for(int i=1;i<=n;++i)        a[i]=read()%7;    build(1,1,n);    int Q=read();    while(Q--)    {        scanf("%s",ch+1);        int x=read(),y=read();        if(ch[1]=='c')            cout<<query(1,1,n,x,y)<<endl;        else         {            int v=read()%7;            modify(1,1,n,x,y,v);        }    }    return 0;}

。。当成是吃一堑长一智吧。。这周搞搞数位DP。。线段树额。。搞搞吧。。
我还想搞KMP和AC自动机来着。。orz

原创粉丝点击