zoukankan      html  css  js  c++  java
  • 【第53套模拟题】【递推】【RMQ】【二进制】【分块】

    题目:(开始自己描述题目了...)

      第一题大意:

        求1~n的所有排列中逆序对为k个的方案数,输出方案数%10000,n<=1000。

      解:这道题一个递推,因为我基本上没怎么自己做过递推,所以推了一个小时,而其实熟练后几分钟十多分钟就推出来了。好吧,我递推的方法:从n=1 开始递推,当n=2的时候由 n=1 推出,以此类推。如何递推?以n=3,k=3为例:有三种方式结尾,以3结尾,前两个数由1,2 排列,3在1,2后面不产生逆序对,那么方案数就等于当n=2的时候产生3个逆序对的方案数,为0 ;以2结尾,2在1,3后面产生1个逆序对,那么方案数就等于当n=2的时候产生2个逆序对的方案数0;以1 结尾,1在2,3后面已产生2个逆序对,那么方案数就等于当n=2的时候产生1个逆序对的方案数1.那么总方案为三种情况的和:1。同样的道理,我们可以得到所有n、k 的情况。。。就自己再推吧,画图比较好理解。。。我就不说了。。

      注意:在取mo的时候,如果前面的式子有 减号,那应该再 +mo 再% mo.... 为此我被坑了70分输出负数。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<map>
     5 #define maxn 1005
     6 #define inf 10000
     7 #define ll long long 
     8 using namespace std;
     9 int t,an[maxn],ak[maxn],man,mak;
    10 int last_k;
    11 ll m[maxn][maxn],sum[maxn][maxn];
    12 int main()
    13 {
    14     freopen("permut.in","r",stdin);
    15     freopen("permut.out","w",stdout);
    16     cin>>t;
    17     for (int i=1;i<=t;i++)
    18     {
    19         scanf("%d%d",&an[i],&ak[i]);
    20         if (an[i]>man) man=an[i];
    21         if (ak[i]>mak) mak=ak[i];
    22     }
    23     last_k=0;
    24     for (int i=1;i<=man;i++)
    25       m[i][0]=sum[i][0]=1;
    26     for (int i=2;i<=man;i++)
    27         {
    28             int j;
    29             for (j=1;j<=last_k+i-1;j++)
    30             {
    31                 if (j>mak) break;
    32                 if (j<=i-1){
    33                     if(j<=last_k){
    34                         m[i][j]=(m[i-1][j]+m[i][j-1])%inf;
    35                     } 
    36                     else m[i][j]=(sum[i-1][last_k])%inf;
    37                 }     
    38                 else 
    39                 {
    40                     if(j<=last_k){
    41                         m[i][j]=((sum[i-1][j]-sum[i-1][j-i])%inf+inf)%inf;//否则输出负数 !! 
    42                     } 
    43                     else m[i][j]=((sum[i-1][last_k]-sum[i-1][j-i])%inf+inf)%inf;//否则输出负数 
    44                 
    45                 }
    46                     
    47                 sum[i][j]=(sum[i][j-1]+m[i][j])%inf;
    48             //    printf("%I64d ",m[i][j]%inf);
    49             }
    50             //cout<<endl;
    51             last_k=j-1;
    52         }
    53     for (int i=1;i<=t;i++)
    54         printf("%I64d
    ",m[an[i]][ak[i]]%inf);    
    55     return 0;
    56 }

      第二题大意:

      有一个数字序列,其中每一个 ai 都有对应的优美值(- -),即找一个包含这个ai的最长的区间,并且ai为这个区间的中位数,那么优美值即这个区间的长度(一定是奇数)。询问对于一个[l,r]的区间中最大的优美值。

      解:1、找优美值。一个数组s。在ai的左右两边,aj如果大于ai,s[j]=1,否则为-1,然后扫描一遍序列,找到最长的区间使s值加起来为0,那么在这个区间内ai肯定是中位数。

        2、查询。可以用线段树,或者RMQ。RMQ更简单一点,就是一个链上倍增。线段树写起来太复杂,下一次来复习。

            3、注意当aj与ai相等时,如果aj在ai左边,则s[j]是-1,右边是+1,所以要注意>和>= 的区别。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #define maxn 2005
     6 using namespace std;
     7 int n,q,num[maxn],be[maxn],f[maxn][16],ma[maxn][16];
     8 int s[maxn],sum[maxn],posl[2*maxn],posr[2*maxn];
     9 void get_beautiful()
    10 {
    11     for (int i=1;i<=n;i++)
    12     {
    13         s[i]=sum[i]=0;
    14         memset(posl,0,sizeof (posl));
    15         memset(posr,0,sizeof (posr));
    16         posl[n]=posr[n]=i;
    17         for (int j=i-1;j>=1;j--)//倒序 
    18         {
    19             if (num[j]>num[i]) s[j]=1;//> 字典序 
    20             else s[j]=-1;
    21             sum[j]=sum[j+1]+s[j];
    22             posl[n+sum[j]]=j;//+
    23         }
    24         for (int j=i+1;j<=n;j++)
    25         {
    26             if (num[j]>=num[i]) s[j]=1;//
    27             else s[j]=-1;
    28             sum[j]=sum[j-1]+s[j];
    29             posr[n+sum[j]]=j;
    30         }
    31         for (int j=0;j<=2*n;j++)
    32           if (posl[2*n-j]&&posr[j])//** posr[2*n-j] **
    33             be[i]=max(be[i],posr[j]-posl[2*n-j]+1);//** posr[2*n-j] **
    34     }
    35 }
    36 void RMQ()
    37 {
    38     for (int i=1;i<=14;i++)
    39       for (int j=1;j<=n;j++)
    40       {
    41             f[j][i]=f[f[j][i-1]][i-1];
    42             ma[j][i]=max(ma[j][i-1],ma[f[j][i-1]][i-1]);
    43       }
    44 }
    45 void query(int l,int r)
    46 {
    47     int ans=0,d=r-l,now=l;
    48     if (l==r)//********
    49     {
    50         printf("%d
    ",be[l]);
    51         return ;
    52     }
    53     for (int i=0;i<=14;i++)
    54     {
    55         if ((1<<i)&d){
    56             ans=max(ma[now][i],ans);
    57             now=f[now][i];
    58         }
    59     }
    60     printf("%d
    ",ans);
    61 }
    62 int main()
    63 {
    64     freopen("beautiful.in","r",stdin);
    65     freopen("beautiful.out","w",stdout);
    66     cin>>n;
    67     for (int i=1;i<=n;i++)
    68       scanf("%d",&num[i]);
    69     get_beautiful();
    70     for (int i=1;i<n;i++)
    71         {
    72             f[i][0]=i+1;
    73             ma[i][0]=max(be[i],be[i+1]);    
    74         }
    75     RMQ();
    76     cin>>q;
    77     for (int i=1;i<=q;i++)
    78     {
    79         int l,r;
    80         scanf("%d%d",&l,&r);
    81         query(l,r);
    82     }
    83     return 0;
    84 }

    第三题大意:

      有三个操作:add 一个数字  delete一个数字,cnt 一个数字s:找数组中ai & s = ai的个数并输出。s<2^16

     解:

      把s分为两块,2^8和2^8。首先要明白:当s的一位上为1 的时候,如果满足ai & s = ai,那么ai那一位上可以为1,0。所以可以说ai是s的子集,s是ai的父集。

      add s的时候用s找它的负集,cnt s的时候找s的子集的个数。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<string>
     5 #include<algorithm>
     6 #define maxn 260
     7 using namespace std;
     8 int q,a[maxn][maxn];
     9 int main()
    10 {
    11     freopen("subset.in","r",stdin);
    12     freopen("subset.out","w",stdout);
    13     cin>>q;
    14     for (int i=1;i<=q;i++)
    15     {
    16         char s[5];
    17         int x,w=1;
    18         scanf("%s%d",s,&x);
    19         if (s[0]=='d') w=-1;
    20         if (s[0]!='c'){
    21             int pre=(x>>8),suf=(x&255),comp=255^suf;//找到s中所有 0,改为 1 
    22             a[pre][suf]+=w;//****
    23             for (int j=comp;j!=0;j=(j-1)&comp)//父集:各种情况的 0 改为 1   
    24                 a[pre][(j|suf)]+=w;
    25         }
    26         else{
    27             int pre=(x>>8),suf=(x&255);
    28             int ans=a[0][suf];
    29             for (int j=pre;j!=0;j=(j-1)&pre)//子集:各种情况的 1 改为 0 
    30               ans+=a[j][suf];
    31             printf("%d
    ",ans);
    32         } 
    33     }
    34     return 0;
    35 }

      

               

  • 相关阅读:
    小程序接入第三方ui库(组件库)
    element ui表格的校验和自定义校验规则
    element ui表格 表头的的特殊处理(换行/jsx风格表头)以及上传组件的一点小问题
    MongoDB 配置本地服务
    乙方渗透测试之Fuzz爆破
    SSRF漏洞挖掘经验
    SQL注入绕过技巧总结
    Xss Bypass备忘录
    bilibili存储型xss (绕过长度限制打乱顺序限制)
    XSS攻击常识及常见的XSS攻击脚本汇总
  • 原文地址:https://www.cnblogs.com/lx0319/p/6014010.html
Copyright © 2011-2022 走看看