模拟11 考试总结
比较。。正常吧
考试经过
一点进去发现这次给了一大堆样例,挺开心
T1看上去是构造题,先想到了取模然后就不会了,看着第三个样例产生了疑惑,就一个数为啥能全出来呢?手玩的过程中想到了gcd,然后惊喜发现输出的就是答案,然后就切了,顺便被绿一次(笑)
T2一眼看出根据拓扑序建图dp,一发离散化之后40带走,然后就不会优化了,想到了单调队列,根本不敢写,然后开T3
T3先冲暴力20,然后推第一个特殊性质,死活推不出来,剩半小时推性质2,发现几乎是白送。。。然后冲上特判,40拿走,坐等出分。。。
估分100+40+40=180,真就一分不差
rank4,%%%上面的战神,赵sir,付队,战神250太可怕了吧……
JYFT2出了半正解有了80高分,土哥似乎T1没有满就仨并列
看了看2018学长,一群上200的。。。
T1.math
我的做法其实是假的
我是对每个数分别对k取模,然后求所有数和\(gcd(a_i,k)\)的最小值就是公差,A了,但战神给了这么一个数据:
3 6
2 3
答案是0到5,而我出来公差是2……
正解使用裴蜀定理
说白了就是求整个序列的gcd,记得最后再和k求一次gcd
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=500500;
int a[N];vector <int> an;
inline int gcd(int x,int y)
{
if(y==0)return x;
else return gcd(y,x%y);
}
signed main()
{
int n,k;cin>>n>>k;
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)a[i]%=k;
int p=k;
for(int i=1;i<=n;i++)if(a[i])p=gcd(p,a[i]);
int ans=0;
for(int i=0;i<k;i+=p)an.push_back(i),ans++;
cout<<ans<<endl;
for(int i=0;i<an.size();i++)printf("%lld ",an[i]);
return 0;
}
我骗了100分,我其实只是一个80的菜鸡。。。
放弃了绿框,因为要相信真理
T2.biology
可以根据拓扑序进行dp,复杂度$n_4&,40分
观察这个dp很容易有状态转移方程
由于这个东西是矩阵,所以他的复杂度下界应该不会少于\(n_2\),我们考虑优化
发现这个max比较耗时间,我们希望把他转化到至少\(log\)级别才行
考虑将绝对值拆开,在左上,左下,右上,右下四个方向上用数据结构维护最值
有关数据结构,二维,你想到了什么?显然树状数组
等等,树状数组能维护最值吗?
如果要处理区间最值问题,树状数组就无能为力了
————林厚从《高级数据结构》
书上说的没错,由于最值不满足区间可加性,所以不能求任意区间的最值,硬要求的话要额外维护\(log\)
但是,我们能求一段前缀的最值!
因为只有前缀不用做差,所以在普通代码里把加变成取max就行了
那么我们要把四个角都变成前缀,怎么办?土哥告诉我们:
将add和query函数里的循环顺序和方向变一下就行了
简直不能再妙,%%%
所以我们写出了四个树状数组,注意各个方向
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=2050;
int a[N][N],b[N][N];
int n,m;
struct node{
int x,y,v;
}c[N*N];int tot,lsh[N*N];
inline void add(int x,int y,int v)
{
c[++tot].x=x;c[tot].y=y;c[tot].v=v;
}
inline int lowbit(int x)
{
return x&(-x);
}
int t1[N][N],t2[N][N],t3[N][N],t4[N][N];
//t1左上t2左下t3右上t4右下
inline void add1(int x,int y,int v)
{
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=m;j+=lowbit(j))
t1[i][j]=max(t1[i][j],v);
}
inline int get1(int x,int y)
{
int s=0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j))
s=max(s,t1[i][j]);
return s;
}
inline void add2(int x,int y,int v)
{
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j;j-=lowbit(j))
t2[i][j]=max(t2[i][j],v);
}
inline int get2(int x,int y)
{
int s=0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j<=m;j+=lowbit(j))
s=max(s,t2[i][j]);
return s;
}
inline void add3(int x,int y,int v)
{
for(int i=x;i;i-=lowbit(i))
for(int j=y;j<=m;j+=lowbit(j))
t3[i][j]=max(t3[i][j],v);
}
inline int get3(int x,int y)
{
int s=0;
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j;j-=lowbit(j))
s=max(s,t3[i][j]);
return s;
}
inline void add4(int x,int y,int v)
{
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j))
t4[i][j]=max(t4[i][j],v);
}
inline int get4(int x,int y)
{
int s=0;
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=m;j+=lowbit(j))
s=max(s,t4[i][j]);
return s;
}
bool cmp(node x,node y)
{
return x.v<y.v;
}
queue <node> q;
inline void doit()
{
while(!q.empty())
{
int x=q.front().x,y=q.front().y,v=q.front().v;
add1(x,y,v-x-y);add2(x,y,v-x+y);
add3(x,y,v+x-y);add4(x,y,v+x+y);
q.pop();
}
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%lld",&a[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%lld",&b[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
add(i,j,a[i][j]);
for(int i=1;i<=tot;i++)lsh[i]=c[i].v;
sort(lsh+1,lsh+tot+1);
int cnt=unique(lsh,lsh+tot+1)-lsh-1;
for(int i=1;i<=tot;i++)
c[i].v=lower_bound(lsh,lsh+cnt+1,c[i].v)-lsh;
sort(c+1,c+1+tot,cmp);
int ans=0;
for(int i=1;i<=tot;i++)
{
if(!c[i].v)continue;
if(c[i].v!=c[i-1].v)doit();
node p;p.x=c[i].x,p.y=c[i].y;
int xx=c[i].x,yy=c[i].y;
if(c[i].v==1)
{
p.v=b[xx][yy];ans=max(ans,p.v);
q.push(p);continue;
}
p.v=max(max(get1(xx,yy)+xx+yy,get2(xx,yy)+xx-yy),max(get3(xx,yy)-xx+yy,get4(xx,yy)-xx-yy))+b[xx][yy];//
ans=max(ans,p.v);
q.push(p);
}
cout<<ans;
return 0;
}
注意拆的时候的正负要和后面的正负号相反,我标记的内行,保证距离最后是差的形式
算法复杂度\(O(nmlog_mlog_n)\),交了之后我们获得了80分的高分
继续优化!把log砍掉 !
实际上感性理解一下,最靠边的店会更优,我们根本不用树状数组,开四个变量记一下就行
#include <bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0'||ch>'9'){ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x;
}
const int N=2050;
int a[N][N],b[N][N];
int n,m;
struct node{
int x,y,v;
}c[N*N];int tot,lsh[N*N];
inline void add(int x,int y,int v)
{
c[++tot].x=x;c[tot].y=y;c[tot].v=v;
}
bool cmp(node x,node y)
{
return x.v<y.v;
}
int p1,p2,p3,p4;
queue <node> q;
inline void doit()
{
while(!q.empty())
{
int x=q.front().x,y=q.front().y,v=q.front().v;
p1=max(p1,v-x-y),p2=max(p2,v-x+y);
p3=max(p3,v+x-y),p4=max(p4,v+x+y);
q.pop();
}
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
b[i][j]=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
add(i,j,a[i][j]);
for(int i=1;i<=tot;i++)lsh[i]=c[i].v;
sort(lsh+1,lsh+tot+1);
int cnt=unique(lsh,lsh+tot+1)-lsh-1;
for(int i=1;i<=tot;i++)
c[i].v=lower_bound(lsh,lsh+cnt+1,c[i].v)-lsh;
sort(c+1,c+1+tot,cmp);
int ans=0;
for(int i=1;i<=tot;i++)
{
if(!c[i].v)continue;
if(c[i].v!=c[i-1].v)doit();
node p;p.x=c[i].x,p.y=c[i].y;
int xx=c[i].x,yy=c[i].y;
if(c[i].v==1)
{
p.v=b[xx][yy];ans=max(ans,p.v);
q.push(p);continue;
}
p.v=max(max(p1+xx+yy,p2+xx-yy),max(p3-xx+yy,p4-xx-yy))+b[xx][yy];
ans=max(ans,p.v);
q.push(p);
}
cout<<ans;
return 0;
}
注意更新的时候因为是分层转移的,所以不能一个一个更新,要批量更新
T3Eglish
暴力20不解释
第二个性质比较好骗,又有20
正解:可持久化01trie
确定是我不会的东西了
咕

浙公网安备 33010602011771号