模拟37 考试总结
犹恐相逢是梦中.

考试经过
考前状态极度低迷,前面两套题基本没动,甚至想过实在状态差就不开题了
然而被迫开题了,一看T1认为很可做,然后打一半发现忘了扩展\(gcd\),于是考场现推无果,最后一度要崩溃。。。努力平复心情之后冲暴力,结果挂的挂炸的炸,出题人部分分没给全,四个小时25\(pts\),基本垫底
不知道该说什么了,一旦这样就几乎一蹶不振,是因为我真的弱吗?
也许不去想结果就不会有遗憾吧,心中压抑的执念,还是 太深了啊...
记得宝玉老师的那句话:想赢的人不一定能赢,不怕输的才会。
把状态拾起来,一切都会过去的,对吗?
对的。
我永远相信的。
T1.数列
会发现其实是让解一个\(ax+by=c\)的方程,所以扩展欧几里德
以前一直以为这玩意只能解同余方程,事实上啥都能解,按公式套就行
发现让最小化\(abs(x)+abs(y)\),于是琢磨啥时候有最小值
由于可以让\(x\),\(y\)同时靠近或远离(一增一减,通解公式),所以只要让\(x\)在正负分别绝对值最小时取两次答案再取\(min\)就行了
更严谨应该在y也分别取两次答案取最小,不过只要保证\(x\)大于\(y\)就行了,如果漏掉会\(WA\)成80的高分
会发现这是一个单峰函数,所以三分也行
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=100050;
int p[N],aa,bb;
inline int exgcd(int a,int b,int &xx,int &yy)
{
if(b==0){xx=1;yy=0;return a;}
int gcd=exgcd(b,a%b,xx,yy);
int tmp=xx;xx=yy;yy=tmp-yy*(a/b);
return gcd;
}
inline int solve(int m)
{
int x,y,gcd=exgcd(aa,bb,x,y);
if(m%gcd){puts("-1");exit(0);}
x*=m/gcd,y*=m/gcd;
int t1=bb/gcd,t2=aa/gcd,k=0;
int ans=1e10;
if(x>0)
{
k=x/t1;x%=t1;y+=k*t2;ans=min(ans,abs(x)+abs(y));
ans=min(ans,abs(x-t1)+abs(y+t2));
}
if(x<0)
{
k=x/-t1;x=x%t1;y-=k*t2;ans=min(ans,abs(x)+abs(y));
ans=min(ans,abs(x+t1)+abs(y-t2));
}
if(x==0)
{
ans=min(ans,abs(x)+abs(y));
ans=min(ans,abs(x+t1)+abs(y-t2));
ans=min(ans,abs(x-t1)+abs(y+t2));
}
return ans;
}
signed main()
{
int n;cin>>n>>aa>>bb;int ans=0;
if(aa>bb)swap(aa,bb);
for(int i=1;i<=n;i++)scanf("%lld",&p[i]);
for(int i=1;i<=n;i++)ans+=solve(p[i]);
cout<<ans;
return 0;
}
T2.数对
其实是原题,唯一不同的是可以有顺序,发现\(a\),\(b\)都大的在前面肯定不优,所以按\(a+b\)排序就行了,然后dp除了符号反一下,加了个u权值之外都没变
本着对自己负责的态度又推了个方程
设\(f_{i,j}\)表示当前处理到第\(i\)个数对,最大的\(A\)为\(j\)的最大收益
然后线段树转移就行,注意边界和一些细节,还有原值继承的问题
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=200050;
struct node{
int a,b,w;
}p[N];
inline bool cmp(node x,node y)
{
return x.a+x.b<y.a+y.b;
}
struct tree{
int l,r,ma,lazy;
}tr[4*N];
inline void luo(int id,int l,int r)
{
if(tr[id].lazy&&l!=r)
{
tr[id*2].ma+=tr[id].lazy;
tr[id*2+1].ma+=tr[id].lazy;
tr[id*2].lazy+=tr[id].lazy;
tr[id*2+1].lazy+=tr[id].lazy;
tr[id].lazy=0;
}
}
inline void change(int id,int l,int r,int p,int v)
{
if(l==r){tr[id].ma=v;return;}
luo(id,l,r);int mid=(l+r)>>1;
if(p<=mid)change(id*2,l,mid,p,v);
else change(id*2+1,mid+1,r,p,v);
tr[id].ma=max(tr[id*2].ma,tr[id*2+1].ma);
}
inline void add(int id,int ll,int rr,int l,int r,int v)
{
if(l>r)return;
if(l<=ll&&r>=rr)
{
tr[id].ma+=v;tr[id].lazy+=v;
return;
}
luo(id,ll,rr);int mid=(ll+rr)>>1;
if(r<=mid)add(id*2,ll,mid,l,r,v);
else if(l>mid)add(id*2+1,mid+1,rr,l,r,v);
else add(id*2,ll,mid,l,mid,v),add(id*2+1,mid+1,rr,mid+1,r,v);
tr[id].ma=max(tr[id*2].ma,tr[id*2+1].ma);
}
inline int getmax(int id,int ll,int rr,int l,int r)
{
if(l>r)return 0;
if(l<=ll&&r>=rr)return tr[id].ma;
luo(id,ll,rr);int mid=(ll+rr)>>1;
if(r<=mid)return getmax(id*2,ll,mid,l,r);
if(l>mid)return getmax(id*2+1,mid+1,rr,l,r);
return max(getmax(id*2,ll,mid,l,mid),getmax(id*2+1,mid+1,rr,mid+1,r));
}
int c[2*N],lsh[2*N];
signed main()
{
int n;cin>>n;
for(int i=1;i<=n;i++)scanf("%lld%lld%lld",&p[i].a,&p[i].b,&p[i].w);
sort(p+1,p+n+1,cmp);
for(int i=1;i<=n;i++)c[i]=p[i].a,c[i+n]=p[i].b;
for(int i=1;i<=2*n;i++)lsh[i]=c[i];
sort(lsh+1,lsh+2*n+1);
int cnt=unique(lsh+1,lsh+2*n+1)-lsh-1;
for(int i=1;i<=2*n;i++)c[i]=lower_bound(lsh+1,lsh+cnt+1,c[i])-lsh;
for(int i=1;i<=n;i++)p[i].a=c[i],p[i].b=c[i+n];
change(1,1,cnt,p[1].a,p[1].w);
for(int i=2;i<=n;i++)
{
if(p[i].a>p[i].b)
{
int maxx=getmax(1,1,cnt,1,p[i].b);
int num=getmax(1,1,cnt,p[i].a,p[i].a);
if(maxx+p[i].w>num)change(1,1,cnt,p[i].a,maxx+p[i].w);
}
else
{
int maxx=getmax(1,1,cnt,1,p[i].a);
add(1,1,cnt,p[i].a,p[i].b,p[i].w);
change(1,1,cnt,p[i].a,maxx+p[i].w);
}
}
int ans=getmax(1,1,cnt,1,cnt);
cout<<ans;
return 0;
}
T3.最小距离
这个多元最短路就很神
暴力是跑\(q\)遍最短路,但是可以在开始时把所有源点都压进去,然后记录每个点是从哪个点转移过来的,相当于记录前趋吧
然后枚举每条边,如果两头的点源点不同,就可以互相更新,用到己方源点的距离+边长+到对方源点的距离更新这个源点的答案
正确性直接看题解:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=200050;
struct node{
int from,to,next,w;
}a[2*N];
int head[N],mm=1;
inline void add(int x,int y,int w)
{
a[mm].from=x;a[mm].to=y;a[mm].w=w;
a[mm].next=head[x];head[x]=mm++;
}
vector <int> sp;
bool v[N];int d[N];
priority_queue <pair<int,int> >q;
int f[N],an[N];
inline void dij()
{
memset(d,0x3f,sizeof(d));
for(int i=0;i<sp.size();i++)
{
int pp=sp[i];d[pp]=0;f[pp]=pp;
q.push(make_pair(0,pp));
}
while(q.size())
{
int x=q.top().second;q.pop();
if(v[x])continue;v[x]=1;
for(int i=head[x];i;i=a[i].next)
{
int y=a[i].to;if(v[y])continue;
if(d[y]>d[x]+a[i].w)
{
d[y]=d[x]+a[i].w;f[y]=f[x];
q.push(make_pair(-d[y],y));
}
}
}
}
signed main()
{
int n,m,p;cin>>n>>m>>p;
for(int i=1;i<=p;i++)
{
int x;scanf("%lld",&x);
sp.push_back(x);
}
for(int i=1;i<=m;i++)
{
int x,y,z;scanf("%lld%lld%lld",&x,&y,&z);
add(x,y,z);add(y,x,z);
}
dij();memset(an,0x3f,sizeof(an));
for(int i=1;i<mm;i+=2)
{
int x=a[i].from,y=a[i].to;
int fx=f[x],fy=f[y];
if(fx==fy)continue;
an[fy]=min(an[fy],d[x]+d[y]+a[i].w);
an[fx]=min(an[fx],d[y]+d[x]+a[i].w);
}
for(int i=0;i<sp.size();i++)printf("%lld ",an[sp[i]]);
return 0;
}
这种思路很好,值得积累
T4.真相
一般经验是这种看着像模拟的题一般都能做,基本赛时拿不了分
A掉的大佬请自动无视
算法标签:枚举
只要判断是否合法,发现大部分是线性关系,即只和下一个有关,关键在第一类断言和全局有关,比较难处理,分情况讨论:
要么没有第一类断言,那么所有断言一定会成环,分别按照第一个人说真话还是说假话顺着推一边,最后能对上(一种就行)就合法,不然非法
要么有第一类断言,发现第一类断言会把整个序列分成若干块,各个块之间不影响,那么根据断言种类可以搞出来一个类似图的东西:

把每个判断拆成两个点,按类型往下连边,最后连出两条链,末端是一类断言,一条真链(末端断言为真),一条假链,我们处理出每条链包含正确的断言数量(即左边的点)\(s\),存进下标为末端一类断言数字的桶里
然后枚举一共几个人讲了真话,判断是否合法,方法是先处理出\(sum\)代表所有假链的\(s\)之和,每次看对应桶中的真链\(s\)减去假链\(s\)加上\(sum\)的值等不等与枚举的值,原因很简单,就看看枚举的符不符合要求就行了,最后记得清空数组
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define py puts("consistent")
#define pn puts("inconsistent")
const int N=1000501;
struct saying{int t,f;}s[N];
int a[N],b[N],n;char c[3];
int sf[N],st[N];bitset <N> v;
stack <int> ss;
inline void clear()
{
while(ss.size())
{int pp=ss.top();s[pp].t=s[pp].f=0;ss.pop();}
for(int i=0;i<=n;i++)a[i]=st[i]=sf[i]=b[i]=0;
for(int i=0;i<=n;i++)v.reset(i);
}
signed main()
{
int t;cin>>t;
while(t--)
{
clear();scanf("%lld",&n);bool flag=0;
for(int i=0;i<n;i++)
{
scanf("%s",c+1);
if(c[1]=='$')scanf("%lld",&a[i]),flag=1;
if(c[1]=='+')a[i]=-2;if(c[1]=='-')a[i]=-1;
}
if(!flag)
{
bool p=0;
for(int i=0;i<n;i++)
{
if(p&&a[i]==-2)p=1;else if(p&&a[i]==-1)p=0;
else if(p==0&&a[i]==-2)p=0;else if(p==0&&a[i]==-1)p=1;
}
if(!p){py;continue;}p=1;
for(int i=0;i<n;i++)
{
if(p&&a[i]==-2)p=1;else if(p&&a[i]==-1)p=0;
else if(p==0&&a[i]==-2)p=0;else if(p==0&&a[i]==-1)p=1;
}
if(p)py;else pn;
}
else
{
int k=0,nn=0;while(a[k]<0)k++;k=(k+1)%n;
for(int i=k;!v[i];i=(i+1)%n)b[++nn]=a[i],v[i]=1;
for(int i=1;i<=n;i++)
{
if(b[i]==-2)st[i+1]=st[i]+1,sf[i+1]=sf[i];
if(b[i]==-1)st[i+1]=sf[i],sf[i+1]=st[i]+1;
if(b[i]>=0)
{
int pp=b[i];ss.push(pp);
s[pp].t+=st[i]+1,s[pp].f+=sf[i];
}
}
int sum=0;bool ok=0;
for(int i=0;i<=n;i++)sum+=s[i].f;
for(int i=0;i<=n;i++)
{
int ans=sum-s[i].f+s[i].t;
if(ans==i){py;ok=1;break;}
}
if(!ok)pn;
}
}
return 0;
}
考试总结
想了很久还觉得,有强大的内心很重要,这正是我所最欠缺的,与此相匹配的是牢靠的基础知识,克服炸裂第一定律的唯一方法是:变得更强。
我们需要时间,而宁静是最好的旅伴.

浙公网安备 33010602011771号