• noip模拟测试12


    老样子,还是先写一下自己的考试经过,首先看题面,觉得开题顺序1 2 3,同时,注意到T2为一个类似于退式子的题,吸取v上次的教训,给他留出足够时间。

      首先是 T1,我又想到了线段树,但是没有想到正解,但是我想到一个 n^n/2 的分组算法,但是比较麻烦,没有分出来,就打了个暴力,一共用时一小时左右

      第二题,一看就是个跟平时数学文化课作业难度差不多的一个概率题,但是我当时看错了 n,m 的输入顺序,导致我的结果和样例一个都对不上,用了差不多一个小时才发现自己把 m,n 看反了

      发现之后就很好说了,五分钟左右就推出了式子,但是这个式子要求取模并约分,我当时就怀疑直接取模计算的正确性,但是没什么好的想法,就直接打了

      T3,我当时想到了贪心,但是我的贪心思路没有正确性,正解的贪心思路真是妙,接下来我对每道题进行详细解析:

      T1:这道题思路来自: https://www.cnblogs.com/hzoi-fengwu

      这道题类似与昨天的T3,同样的,我们可以利用单调队列求出每个 a[i] ,当作最大值的左右区间,区间没有交集,并利用启发式合并的思想,暴力枚举小的区间,理智处理较大区间,

      这样就可以省去枚举左右端点。利用入阵曲的思路,若 a ,b,在 %p 意义下同余,(设 a>b ) 则 (a-b) 可以被 p 整除。

      那么我们就可以利用一个vector数组,第一维存模,第二维存元素的位置,以上就是预处理的过程

      接下来就是一个非常秒的计算,我们以左区间小于右区间为例,我们从 [L,p],枚举每一个sum[i-1],因为满足条件的右端点 x 必定满足 ,sum[x]==((sum[i-1]+a[p])%k),

      我们设 X=((sum[i-1]+a[p])%k),那么我们通过计算Y=lower_bound(v[X].begin(),v[X].end(),p)-v[X].begin(),和Z=upper_bound(v[X].begin(),v[X].end(),R)-v[X].begin();做差即可求出满足条件的右端点个数

      最后 ans+=Z-Y即可

      注意,当左区间大于右区间时,因为我们要查询 Y=lower_bound(v[X].begin(),v[X].end(),L-1)-v[X].begin(),当 L==1时,会查到0,所以我们对于 v[0].push_back(0),意思就是前0位的前缀和为 0,%p后余数也为零

      代码如下:

      

      1 #include<bits/stdc++.h>
      2 #define int long long
      3 #define re register int
      4 #define iv inline void
      5 #define ii inline int
      6 #define lc rt<<1
      7 #define rc rt<<1|1
      8 #define mid ((l+r)>>1)
      9 using namespace std;
     10 const int N=5e6+10;
     11 int n,k;
     12 int a[N],l[N],r[N],sum[N],ans;
     13 stack <pair<int,int> > s;
     14 vector <int> v[N];
     15 ii read()
     16 {
     17     int x=0,f=1;
     18     char ch=getchar();
     19     while(ch<'0'||ch>'9')
     20     {
     21         if(ch=='-')
     22             f=0;
     23         ch=getchar();
     24     }
     25     while(ch>='0'&&ch<='9')
     26     {
     27         x=(x<<1)+(x<<3)+(ch^48);
     28         ch=getchar();
     29     }
     30     return (f)?x:(-x);
     31 }
     32 void in()
     33 {
     34     for(re i=1;i<=n;i++)
     35     {
     36         while(!s.empty()&&a[i]>=s.top().first)
     37         {
     38             r[s.top().second]=i-1;
     39             s.pop();
     40         }
     41         s.push(make_pair(a[i],i));
     42     }
     43     int L=s.top().second;
     44     while(!s.empty())
     45     {
     46         r[s.top().second]=L;
     47         s.pop();
     48     }
     49     for(re i=n;i;i--)
     50     {
     51         while(!s.empty()&&a[i]>s.top().first)
     52         {
     53             l[s.top().second]=i+1;
     54             s.pop();
     55         }
     56         s.push(make_pair(a[i],i));
     57     }
     58     int R=s.top().second;
     59     while(!s.empty())
     60     {
     61         l[s.top().second]=R;
     62         s.pop();
     63     }
     64 }
     65 iv workk(int p,int L,int R)
     66 {
     67     if(p-L<R-p)
     68     {
     69         int X,Y,Z;
     70         for(re i=L;i<=p;i++)
     71         {
     72             X=(sum[i-1]+a[p])%k;
     73             Y=lower_bound(v[X].begin(),v[X].end(),p)-v[X].begin();
     74             Z=upper_bound(v[X].begin(),v[X].end(),R)-v[X].begin();
     75             ans+=max(0*1ll,Z-Y);    
     76         }
     77     }
     78     else
     79     {
     80         int X,Y,Z;
     81         for(re i=p;i<=R;i++)
     82         {
     83             X=(sum[i]-a[p]%k+k)%k;
     84             Y=lower_bound(v[X].begin(),v[X].end(),L-1)-v[X].begin();
     85             Z=upper_bound(v[X].begin(),v[X].end(),p-1)-v[X].begin();
     86             ans+=max(0ll,Z-Y);
     87         }
     88     }
     89 }
     90 #undef int
     91 int main()
     92 {
     93     #define int long long
     94     n=read();
     95     k=read();
     96     for(re i=1;i<=n;i++)
     97         a[i]=read();
     98     in();
     99     for(re i=0;i<k;i++)
    100         v[i].push_back(0);
    101     for(re i=1;i<=n;i++)
    102         sum[i]=(sum[i-1]+a[i])%k;
    103     for(re i=1;i<=n;i++)
    104         v[sum[i]%k].push_back(i);
    105     for(re i=1;i<=n;i++)
    106         workk(i,l[i],r[i]);    
    107     ans-=n;
    108     printf("%lld\n",ans-());
    109     return 0;
    110 }

    T2

      这道题式子很好推,就是   ( ( C^m,(2^n) ) *m! )/2^(n*m), == ( ((2^n)!) / (((2^n)-m)!) )/(2^(n*m));

      上面的部分就是 (2^n) *((2^n) -1) *((2^n)-2)* ..... *((2^n)-m+1) , 也就是m 个连续的数相乘

      那么显然,当 m>mo 时,分子就是 0

      接下来我们考虑约分,这道题麻烦的地方就在于不能用 gcd ,因为模了一个数后结果会不对

      有这样一个结论,

          

       

      那么分子就转化成求 (m-1)!  中的2的因子数

      可以这样求:

       for(re i=2;i<m;i<<=1) ans+=(m-1)/i; 

       

      

      最后,我们利用逆元的性质求出答案即可

      代码如下:

    #include<bits/stdc++.h>
    #define int long long
    #define re register int
    #define iv inline void
    #define ii inline int
    #define lc rt<<1
    #define rc rt<<1|1
    #define mid ((l+r)>>1)
    using namespace std;
    int m,n,sum,U,D;
    int mo=1e6+3;
    inline long long read()
    {
        int x=0,f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9')
        {
            if(ch=='-')
                f=0;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return (f)?x:(-x);
    }
    ii ksm(int d,int z)
    {
        int out=1;
        while(z)
        {
            if(z&1)
                out=out*d%mo;
            z>>=1;
            d=d*d%mo;    
        }
        return out%mo;
    }
    #undef int
    int main()
    {
        #define int long long
        n=read();
        m=read();
        if(log2(m)>n)
        {
            printf("1 1\n");
            return 0;
        }
        U=1,D=1;
        int base=ksm(2,n),ans=0,inv;
        D=ksm(base,m-1);
        for(re i=2;i<m;i<<=1)
            ans+=(m-1)/i;
        inv=ksm(ksm(2,ans),mo-2);
        if(m<=mo)
            for(re i=1;i<m;i++)
                U=U*(base-i)%mo;
        else
            U=0;
        U=(U%mo*inv%mo+mo)%mo;
        D=(D%mo*inv%mo+mo)%mo;
        printf("%lld %lld\n",((D-U+mo)%mo+mo)%mo,(D+mo)%mo);
        return 0;    
    }

    T3

    一道思路非常妙的贪心题

      那么我们就开两个二元组,up,down,按照上述思路操作即可

      具体来说,我们先正着扫一遍,先不考虑是否当前位置有数,继承up 和 down

      up两位一进,down五位一进
      考虑已经填的,先考虑上界,若a[i]>up,比上界大肯定不合法,若a[i]=up,不管,若a[i]<up,则将up调整到a[i]次数变为2,下界类似,若a[i]<down,比下界小不合法,若a[i]>down,将down调整到a[i],统计答案时反着扫

      最后输出的时候记得右端点特判

    代码如下:

      1 #include<bits/stdc++.h>
      2 #define int long long
      3 #define re register int
      4 #define iv inline void
      5 #define ii inline int
      6 #define lc rt<<1
      7 #define rc rt<<1|1
      8 #define mid ((l+r)>>1)
      9 using namespace std;
     10 const int N=2e5+10;
     11 int n,ans=-1,maxn;
     12 int a[N],vis[N],v[N],cun[N];
     13 pair<int,int> up[N],down[N];
     14 ii read()
     15 {
     16     int x=0,f=1;
     17     char ch=getchar();
     18     while(ch<'0'||ch>'9')
     19     {
     20         if(ch=='-')
     21             f=0;
     22         ch=getchar();
     23     }
     24     while(ch>='0'&&ch<='9')
     25     {
     26         x=(x<<1)+(x<<3)+(ch^48);
     27         ch=getchar();
     28     }
     29     return (f)?x:(-x);
     30 }
     31 #undef int
     32 int main()
     33 {
     34     #define int long long
     35     n=read();
     36     for(re i=1;i<=n;i++)
     37         a[i]=read(),vis[a[i]]++;
     38     up[1].first=up[1].second=down[1].first=down[1].second=1;
     39     bool noo=0;
     40     for(re i=2;i<=n;i++)
     41     {
     42         if(up[i-1].second==2)
     43         {
     44             up[i].first=up[i-1].first+1;
     45             up[i].second=1;
     46         }
     47         else
     48         {
     49             up[i].first=up[i-1].first;
     50             up[i].second=up[i-1].second+1;
     51         }
     52         if(down[i-1].second==5)
     53         {
     54             down[i].first=down[i-1].first+1;
     55             down[i].second=1;
     56         }
     57         else
     58         {
     59             down[i].first=down[i-1].first;
     60             down[i].second=down[i-1].second+1;
     61         }
     62         if(a[i])
     63         {
     64             if(a[i]>up[i].first)
     65             {
     66                 noo=1;
     67                 break;
     68             }
     69             if(a[i]<down[i].first)
     70             {
     71                 noo=1;
     72                 break;
     73             }
     74             if(a[i]<up[i].first)
     75             {
     76                 up[i].first=a[i];
     77                 up[i].second=2;
     78             }
     79             if(a[i]>down[i].first)
     80             {
     81                 down[i].first=a[i];
     82                 down[i].second=1;
     83             }
     84         }
     85     }
     86     if(noo)
     87     {
     88         printf("-1\n");
     89         return 0;
     90     }
     91     if(a[n])
     92         cun[n]=a[n];
     93     else
     94     {
     95         cun[n]=up[n-1].first;
     96         ++vis[cun[n]];
     97     }
     98     for(re i=n-1;i;i--)
     99     {
    100         if(a[i])
    101             cun[i]=a[i];
    102         else
    103         {
    104             int tmp=min(up[i].first,cun[i+1]);
    105             while(vis[tmp]==5)
    106                 --tmp;
    107             cun[i]=tmp;
    108             ++vis[cun[i]];
    109         }
    110     }
    111     printf("%lld\n",cun[n]);
    112     for(re i=1;i<=n;i++)
    113         printf("%lld ",cun[i]);
    114     return 0;    
    115 }
  • 相关阅读:
    Exception in thread "main" com.sun.xml.internal.ws.streaming.XMLStreamReaderException: unexpected XML tag.
    Navicat的快捷键
    win7虚拟机起不来,报错transport vmdb error -44 message the vmware authorization
    fedora19配置 SSH 免密码登陆
    Linux下Django的安装
    linux下为用户添加sudo命令功能
    ubuntu修改系统环境变量文件导致起不来
    fedorea19安装redis
    java下载csv文件,中文标题
    POJ A Simple Problem with Integers | 线段树基础练习
  • 原文地址:https://www.cnblogs.com/WindZR/p/15004115.html
Copyright © 2020-2023  润新知