zoukankan      html  css  js  c++  java
  • 8.23<2>题解

    由于考试第二天就滚回文化课了,所以一直在翘自习改考试题,改到昨天考试之前才刚刚改完,以后就是半集训了,博客可能会经常性咕咕咕,有空会填坑

    T1

    看这道题的随机生成,其实有点懵,就直接甩到了最后,没来得及看大样例,也没想到可以找规律,于是乎打了20分暴力就滚粗了,实施上如果打开了大样例的话,应该会发现一些猫腻,会发现答案是一个等差数列,而公差就是所有的$a_i$和$k$的$gcd$的最小值,就结束了,证明没怎么看,来自出题人的证明

     1 //每次+最小gcd加到k
     2 #include<iostream>
     3 #include<cstdio>
     4 using namespace std;
     5 int n,mod,minn=1000100,ans;
     6 int pd[1001000];
     7 int gcd(int a,int b)
     8 {
     9     return b==0?a:gcd(b,a%b);
    10 }
    11 int main()
    12 {
    13     scanf("%d%d",&n,&mod);
    14     for(int i=1;i<=n;++i)
    15     {
    16         int x;  scanf("%d",&x);
    17         pd[x%mod]=1;
    18     }
    19     for(int i=0;i<=mod;++i)
    20         if(pd[i])  minn=min(minn,gcd(mod,i));
    21     printf("%d
    0 ",mod/minn);
    22     int ls=minn;
    23     while(ls<mod)  {printf("%d ",ls);  ls+=minn;}
    24     puts("");
    25     return 0;
    26 }
    View Code

    T2

    考场上想到了dp转移,也打出来了,想到了二维树状数组维护最值,但是由于觉得二维树状数组只能维护前缀最大值,所以并没有打,但是正解中的转换坐标的思路,真的非常好

    我把一个矩阵直接压成了一个数列,只是会存一下他们的坐标,所以二维的dp也同时被我压成了一维,由于他需要$a$单调递增,所以对于当前点只会由$a[j]=a[i]-1$的$j$转移而来,那么此时$dp[i]=max(dp[j]+b[i]+|x[i]-x[j]|+|y[i]-y[j]|)$,之所以需要二维树状数组,是由于横纵坐标分别去绝对值对应了4种情况,那我们把原坐标$(i,j)$,变成$(i+j,i-j)$,就可以直接维护这四种情况,如果绝对值去的不正确那么答案只会更差,所以我们只需要维护对于每个点的四种情况,直接$O(1)$查询最大值即可,而我们需要维护的就是所有$a[i]-1$的点的$dp[j]-x$ $dp[j]-y$ $dp[j]+x$ $dp[j]+y$,$x$和$y$均为修改后的坐标,可以用数组维护,也可以只开四个变量,就可以做到$O(1)$查询,最坏情况下,总复杂度就是$O(n^2)$

    记得按照a排序

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstdio>
     4 #define re register
     5 #define ll long long
     6 #define maxn 2010
     7 using namespace std;
     8 struct node{
     9     int a,b,x,y;
    10 }tz[maxn*maxn];
    11 struct tr{
    12     int zuo,you;
    13     ll w;
    14 }tre1[maxn*4],tre2[maxn*4];
    15 int n,m,js,flag=1;
    16 ll max1,max2,max3,max4,ls1,ls2,ls3,ls4,ans;
    17 ll dp[maxn*maxn];
    18 int aa[maxn][maxn],bb[maxn][maxn];
    19 inline int read()
    20 {
    21     re int e=0;  re char ch=getchar();
    22     while(ch<'0'||ch>'9')  ch=getchar();
    23     while(ch>='0'&&ch<='9')  {e=(e<<3)+(e<<1)+(ch^48);  ch=getchar();}
    24     return e;
    25 }
    26 bool cmp(const node &a,const node &b)
    27 {
    28     return a.a<b.a;
    29 }
    30 int main()
    31 {
    32     n=read();  m=read();
    33     for(re int i=1;i<=n;++i)
    34         for(re int j=1;j<=m;++j)  aa[i][j]=read();
    35     for(re int i=1;i<=n;++i)
    36         for(re int j=1;j<=m;++j)  bb[i][j]=read();
    37     for(re int i=1;i<=n;++i)
    38         for(re int j=1;j<=m;++j)
    39         {
    40             if(aa[i][j]==0)  continue;
    41             tz[++js].a=aa[i][j];  tz[js].b=bb[i][j];
    42             tz[js].x=i+j;  tz[js].y=i-j;
    43         }
    44     sort(tz+1,tz+js+1,cmp);
    45     re int qd=1;  dp[1]=1ll*tz[1].b;  ans=dp[1];
    46     max1=dp[1]+tz[1].x;  max2=dp[1]-tz[1].x;
    47     max3=dp[1]+tz[1].y;  max4=dp[1]-tz[1].y;
    48     for(re int i=2;i<=js;++i)
    49     {
    50         if(tz[i].a!=tz[i-1].a)  break;
    51         qd=i;  dp[i]=1ll*tz[i].b;
    52         max1=max(max1,dp[i]+tz[i].x);  max2=max(max2,dp[i]-tz[i].x);
    53         max3=max(max3,dp[i]+tz[i].y);  max4=max(max4,dp[i]-tz[i].y);
    54         ans=max(ans,dp[i]);
    55     }
    56     qd++;
    57     for(re int i=qd;i<=js;++i)
    58     {
    59         if(tz[i].a!=tz[i-1].a)
    60         {
    61             ls1=max1;  ls2=max2;  ls3=max3;  ls4=max4;
    62             max1=0;  max2=0;  max3=0;  max4=0;
    63         }
    64         ll lss1=max(ls1-tz[i].x,ls2+tz[i].x);
    65         ll lss2=max(ls3-tz[i].y,ls4+tz[i].y);
    66         dp[i]=max(lss1,lss2)+tz[i].b;
    67         max1=max(max1,dp[i]+tz[i].x);  max2=max(max2,dp[i]-tz[i].x);
    68         max3=max(max3,dp[i]+tz[i].y);  max4=max(max4,dp[i]-tz[i].y);
    69         ans=max(ans,dp[i]);
    70     }
    71     printf("%lld
    ",ans);
    72     return 0;
    73 }
    View Code

    T3

    预处理类似于8.22的T2,找到以这个值作为最大值的最大区间,在区间中算贡献

    $ans1$其实还是比较简单的,由于我们已经确定了最大值管辖的区间,那对于异或,我们可以按位考虑贡献,对于最大值左侧如果第$j$为上为1,那就要求右区间的这一位必须是0,才能作出贡献,那么我们可以前缀和$f[i][j]$统计前$i$个数中第$j$位上为1的有几个数,左右乘起来计算贡献即可

    $ans2$正解给出的方法是启发式合并$01trie$,我不会,于是就去学了可持久化$01trie$,看懂了的话其实挺好理解的,下面给出模板

     1 struct trie{
     2     int cnt;
     3     int ch[maxn][2],sum[maxn];
     4     int insert(int x,int val)
     5     {
     6         int tmp,y;  tmp=y=++cnt;
     7         for(int i=27;i>=0;i--)
     8         {
     9             ch[y][0]=ch[x][0];  ch[y][1]=ch[x][1];
    10             int t=val&mi[i];  t>>=i;
    11             x=ch[x][t];  ch[y][t]=++cnt;  y=ch[y][t];
    12             sum[y]=sum[x]+1;
    13         }
    14         return tmp;
    15     }
    16 }trie;

    我并没有把询问放出来,因为询问其实挺看题的,可以自己yy,$root[i]$记录的是历史版本,对于这道题来说就是一个数代表一个历史版本,$sum[i]$记录的是有多少个数经过了当前结点,其他的就是继承以及新插入了

    拿这道题就变为了查找在当前区间中,左右区间异或大于最大值的两个端点,借用启发式合并的思维,我们枚举较小的区间中的每一个数,去查另一个区间中有几个数和它异或起来大于最大值,我们在$trie$上按位查找,如果异或上这一位已经大于最大值就加上这一位上的$sum$,如果小于就直接停了,否则按位查询下去即可

    最后统计答案就可以了

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<stack>
      4 #define int long long
      5 #define maxn 5001000
      6 #define mod 1000000007
      7 #define inf 10000000
      8 using namespace std;
      9 int n,opt,ans1,ans2;
     10 int a[maxn],l[maxn],r[maxn],root[maxn],mi[28];
     11 int f[maxn][28];
     12 stack <int> s;
     13 struct trie{
     14     int cnt;
     15     int ch[maxn][2],sum[maxn];
     16     int insert(int x,int val)
     17     {
     18         int tmp,y;  tmp=y=++cnt;
     19         for(int i=27;i>=0;i--)
     20         {
     21             ch[y][0]=ch[x][0];  ch[y][1]=ch[x][1];
     22             int t=val&mi[i];  t>>=i;
     23             x=ch[x][t];  ch[y][t]=++cnt;  y=ch[y][t];
     24             sum[y]=sum[x]+1;
     25         }
     26         return tmp;
     27     }
     28     //被查询点,当前在查询第几层,比较的a[i],某一个端点a[j]
     29     int quary(int fa,int pos,int base,int cs,int tot)
     30     {
     31         if(pos<0)  return 0;
     32         int lsan=0;
     33         int s1=(((cs>>pos)&1)^0)*mi[pos],s2=(((cs>>pos)&1)^1)*mi[pos];
     34         int dq=(base>>pos)&1;
     35         int yh0=((cs>>pos)&1)^0,yh1=((cs>>pos)&1)^1;
     36         if(dq==0)
     37         {
     38             if(yh0==0)
     39             {
     40                 lsan+=sum[ch[fa][1]];
     41                 lsan+=quary(ch[fa][0],pos-1,base,cs,tot+s1);
     42             }
     43             else
     44             {
     45                 lsan+=sum[ch[fa][0]];
     46                 lsan+=quary(ch[fa][1],pos-1,base,cs,tot+s2);
     47             }
     48         }
     49         else
     50         {
     51             if(yh0==0)  lsan+=quary(ch[fa][1],pos-1,base,cs,tot+s2);
     52             else  lsan+=quary(ch[fa][0],pos-1,base,cs,tot+s1);
     53         }
     54         return lsan;
     55     }
     56 }trie;
     57 void work1()
     58 {
     59     for(int i=1;i<=n;++i)
     60     {
     61         for(int j=0;j<=27;++j)
     62         {
     63             if(((a[i]>>j)&1)==1)  f[i][j]=f[i-1][j]+1;
     64             else  f[i][j]=f[i-1][j];
     65         }
     66     }
     67     for(int i=1;i<=n;++i)
     68     {
     69         int ls=1ll*0;
     70         for(int j=0;j<=27;++j)
     71         {
     72             int ls1=(f[i][j]-f[l[i]-1][j])%mod;
     73             int ls2=(f[r[i]][j]-f[i-1][j])%mod;
     74             int len1=(i-l[i]+1)%mod,len2=(r[i]-i+1)%mod;
     75             ls=(ls+(mi[j]%mod*(ls1*((len2-ls2+mod)%mod))%mod)%mod)%mod;
     76             ls=(ls+(mi[j]%mod*(((len1-ls1+mod)%mod)*ls2)%mod)%mod)%mod;
     77             ls=ls%mod;
     78         }
     79         ans1=(ans1+(ls*a[i]%mod)%mod)%mod;
     80     }
     81     printf("%lld
    ",ans1);
     82 }
     83 void work2()//查询有几个数,就有几倍的a[i]
     84 {
     85     for(int i=1;i<=n;++i)  root[i]=trie.insert(root[i-1],a[i]);
     86     for(int i=1;i<=n;++i)
     87     {
     88         if(i-l[i]+1<r[i]-i+1)
     89         {
     90             for(int j=l[i];j<=i;++j)
     91             {
     92                 int ls1=trie.quary(root[r[i]],27,a[i],a[j],0);
     93                 int ls2=trie.quary(root[i-1],27,a[i],a[j],0);
     94                 ans2=(ans2+(((ls1-ls2)%mod)*a[i])%mod)%mod;
     95             }
     96         }
     97         else
     98         {
     99             for(int j=i;j<=r[i];++j)
    100             {
    101                 int ls1=trie.quary(root[i],27,a[i],a[j],0);
    102                 int ls2=trie.quary(root[l[i]-1],27,a[i],a[j],0);
    103                 ans2=(ans2+(((ls1-ls2)%mod)*a[i])%mod)%mod;
    104             }
    105         }
    106     }
    107     printf("%lld
    ",ans2);
    108 }
    109 main()
    110 {
    111 //    freopen("english6.in","r",stdin);
    112     scanf("%lld%lld",&n,&opt);  mi[0]=1;
    113     for(int i=1;i<=27;++i)  mi[i]=mi[i-1]*2;
    114     for(int i=1;i<=n;++i)  scanf("%lld",&a[i]);
    115     a[0]=inf;  a[n+1]=inf;
    116     s.push(0);
    117     for(int i=1;i<=n;++i)
    118     {
    119         while(a[s.top()]<a[i])  s.pop();
    120         l[i]=s.top()+1;  s.push(i);
    121     }
    122     while(s.size())  s.pop();
    123     s.push(n+1);
    124     for(int i=n;i>=1;--i)
    125     {
    126         while(a[s.top()]<=a[i])  s.pop();
    127         r[i]=s.top()-1;  s.push(i);
    128     }
    129     if(opt==1)  work1();
    130     else if(opt==2)  work2();
    131     else  {work1();  work2();}
    132     return 0;
    133 }
    View Code
  • 相关阅读:
    058_从键盘读取一个论坛积分,判断论坛用户等级
    057_统计 Linux 进程相关数量信息
    bzoj3436
    bzoj1202
    bzoj1044
    bzoj2338
    bzoj1854
    bzoj1856
    830C
    bzoj2132
  • 原文地址:https://www.cnblogs.com/hzjuruo/p/11428963.html
Copyright © 2011-2022 走看看