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\n0 ",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\n",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\n",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\n",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

 

posted @ 2019-08-29 12:04  hzoi_X&R  阅读(204)  评论(0编辑  收藏  举报