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 }

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 }

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 }

原文地址:https://www.cnblogs.com/hzjuruo/p/11428963.html

时间: 2024-10-16 01:04:37

8.23<2>题解的相关文章

8.23&lt;1&gt;题解

T1 考场上全程盯着PDF,不知道OJ上有样例解释?最后题都没读懂,看到OJ上的样例解释之后恍然大悟,然后就过了,我×××××,下次我一定PDF和OJ题面都认真阅读 读懂题之后其实很简单,给序列排序,去重,然后在排好序的数组中寻找原序列前一位和后一位,看是否满足条件就可以了 1 #include<algorithm> 2 #include<iostream> 3 #include<cstdio> 4 #define int long long 5 #define max

内部考试总结

2019年8月5日星期一 Kos 题目描述 有\(n\)个人,他们之间将会有\(m\)场比赛(两个人之间可能有多场比赛,但这些比赛的结果不一定完全相同),问获胜场次最多的人获胜的场次数最少 限制范围 \(Time:1000ms\quad Space:512MB\) \(1\le n\le 10^4,0\le m\le 10^4\) 正解算法 建立二分答案,对猜测解\(x\),考虑建立最大流模型 对每场比赛和每个人都建立一个点,从源点向每场比赛连一条容量为\(1\)的边,每个选手向汇点连一条容量为

[考试反思]0220省选模拟27:怪异

怪异的一场考试.考的有点懵 $T1$正解是模拟,人人都能切,然而也没有多少人敢笃定这就是正解吧,常数和精度上下卡着,看运气的题了. $T2$想到了第二档分其实离正解就不远了但是时间不够没往下想.回$T1$卡常去了. $T3$不给状压分,于是猜到一条结论之后并不敢往下想... 然而最后$T1$也没有卡过,考后也在这破玩意上浪费了不少时间... 而且$T3$数据特别谁水暴力可以过,然而因为我不会决策点单调,所以学了学知识点,并没有用暴力水过... T1:飞行棋 大意:长度为$n$的序列,$m$人.每

Leetcode题解(23)

69. Sqrt(x) 题目 分析,题目实现求一个int数的平方根,最暴力的算法就是逐个遍历,从1开始到x,判断是否有一个数i,其中满足i*i<=x,并且(i+1)*(i+1)>x;这个算法发虽然简单,但是效率不高.其实,按照暴力算法的思想,我们可以联想到在一个已经排好序的数组中查找一个数,该数正好满足上面的条件,因此可以采用二分查找的思想.代码如下: 1 class Solution { 2 public: 3 int mySqrt(int x) { 4 int a[10]={0,1,1,1

2019.10.23题解

A. Smooth 标签: 队列 题解: 直接口胡一下算法过程吧: 开B个队列,先在第一个队列里加入1,之后每次取出这B个队列里最小的数x, x便为最小的光滑数,i从小到大枚举并用x*p[i]更新第i个队列, 为了不重复,要用每个数的最小质因子更新即当p[i]|x停止, 因为x一定比上次取出的y要大,所以可以保证每个队列都是单调的, 复杂度$ O(BK) $ B. Six 标签: 记忆化搜索 题解: 设f[i][j]代表已选的质因子集合为i,出现在两个不同的数里的质因子对集合为j的方案数, 记忆

省选模拟23 题解

这篇题解鸽了. A. Expectation B. Sequence C. Counting 原文地址:https://www.cnblogs.com/skyh/p/12309632.html

2016.4.23 浙江省赛题解

Apples and Ideas Time Limit: 2 Seconds      Memory Limit: 65536 KB "If you have an apple and I have an apple and we exchange these apples then you and I will still each have one apple. But if you have an idea and I have an idea and we exchange these

洛谷 P1079 Vigen&#232;re 密码 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1079 题目描述 16 世纪法国外交家 Blaise de Vigenère 设计了一种多表密码加密算法――Vigenère 密 码.Vigenère 密码的加密解密算法简单易用,且破译难度比较高,曾在美国南北战争中为 南军所广泛使用. 在密码学中,我们称需要加密的信息为明文,用 M 表示:称加密后的信息为密文,用 C 表示:而密钥是一种

2017ZZUACM省赛选拔试题部分题解----谨以纪念我这卡线滚粗的美好经历

写在前面: 其实心里有些小小的不爽又有点小小的舒畅,为啥捏?不爽当然是因为没被选拔上啦,舒畅捏则是因为没被选拔上反而让自己警醒,学长也提点很多很多."沉下去,然后一战成名"学长如是对我说,我很开心.其实这完全算不算是题解,只是我个人的一些小想法而已.而且到现在还有一题不会...让自己长点记性吧. 题目 A :聪明的田鼠 Time Limit: 1 Sec Memory Limit: 128 MB Description 田鼠MIUMIU来到了一片农田,农田可以看成是一个M*N个方格的矩