Test 5 for NOIP - 2017.7.24.

头大

这个暑假完就要去搞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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值