CSP-S 模拟测试92 题解
话说我怎么觉得我没咕多长时间啊,怎么就又落了20多场题解啊
T1 array:
根据题意不难列出二元一次方程,于是可以用exgcd求解,然而还有一个限制条件就是$abs(x)+abs(y)$最小,这好像很难搞,但是我们用exgcd求出一组特解之后的通解公式是个一次函数,通过手玩可知x,y只可能是最小正整数或最大负整数解,这就很好整,时间复杂度$O(n)$,当然也可以三分。
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 const int N=1e5+10; 5 void exgcd(int a,int b,int &x,int &y,int &gcd){//ax+by=c,gcd wei gongyueshu 6 if(!b){ 7 gcd=a; 8 x=1; 9 y=0; 10 } 11 else{ 12 exgcd(b,a%b,y,x,gcd); 13 y-=a/b*x; 14 } 15 } 16 int gcd(int a,int b){ 17 return (!b)?a:gcd(b,a%b); 18 } 19 int solve(int a,int b,int x,int y,int k,int g){ 20 //cout<<"a=="<<a<<" b=="<<b<<" k=="<<k<<endl; 21 int ka=a,kb=b; 22 a/=g,b/=g;k/=g; 23 //cout<<"a=="<<a<<" b=="<<b<<" k=="<<k<<endl; 24 int res=0x7fffffffffffffff; 25 int tmpx=x%b,tmpy=(k-a*tmpx)/b; 26 res=min(res,abs(tmpx)+abs(tmpy)); 27 //cout<<"tmpx=="<<tmpx<<" tmpy=="<<tmpy<<" res=="<<res<<endl; 28 if(tmpx==0){ 29 res=min(res,abs(tmpx+b)+abs(tmpy-a)); 30 res=min(res,abs(tmpx-b)+abs(tmpy+a)); 31 } 32 33 else if(tmpx<0) tmpx+=b,tmpy-=a,res=min(res,abs(tmpx)+abs(tmpy)); 34 else tmpx-=b,tmpy+=a,res=min(res,abs(tmpx)+abs(tmpy)); 35 //cout<<"tmpx=="<<tmpx<<" tmpy=="<<tmpy<<" res=="<<res<<endl; 36 tmpy=y%a,tmpx=(k-b*tmpy)/a; 37 res=min(res,abs(tmpx)+abs(tmpy)); 38 //cout<<"tmpx=="<<tmpx<<" tmpy=="<<tmpy<<" res=="<<res<<endl; 39 if(tmpy==0){ 40 res=min(res,abs(tmpx+b)+abs(tmpy-a)); 41 res=min(res,abs(tmpx-b)+abs(tmpy+a)); 42 } 43 else if(tmpy<0) tmpy+=a,tmpx-=b,res=min(res,abs(tmpx)+abs(tmpy)); 44 else tmpy-=a,tmpx+=b,res=min(res,abs(tmpx)+abs(tmpy)); 45 //cout<<"tmpx=="<<tmpx<<" tmpy=="<<tmpy<<" res=="<<res<<endl; 46 return res; 47 } 48 int k[N]; 49 main(){ 50 //freopen("1.in","r",stdin); 51 int n,a,b; 52 scanf("%lld%lld%lld",&n,&a,&b); 53 for(int i=1;i<=n;++i) scanf("%lld",&k[i]); 54 int flag=0; 55 long long ans=0; 56 int x,y; 57 int g; 58 exgcd(a,b,x,y,g); 59 int tmpx=x,tmpy=y; 60 for(int i=1;i<=n;++i){ 61 //cout<<"x=="<< x<<" y=="<<y<<endl; 62 //cout<<"gcd=="<<g<<" k[i]=="<<k[i]<<endl; 63 if(k[i]%g!=0) {flag=1;break;} 64 int tmp=k[i]/g; 65 x=tmpx*tmp,y=tmpy*tmp; 66 //cout<<"x=="<<x<<" y=="<<y<<endl; 67 int res=solve(a,b,x,y,k[i],g); 68 //cout<<"res=="<<res<<endl; 69 ans+=res; 70 } 71 if(flag) puts("-1"); 72 else printf("%lld\n",ans); 73 return 0; 74 }
T2 pair
考场上一看到这题发现和队长快跑那题很像,然后就回忆起了那天调了一个下午影魔的一个log在线做法然后水果那道题的惨痛经历,果然水题没有好下场啊qwq
首先设出dp定义$dp_{i,s}$表示到i并且选i,$max(a_i)=j$的最大值。
我们在来考虑他给出的限制,如果$a_i < b_j$且 $a_j>b_i$ 那么一定是i在j前。反之同理。
那么我们可以按$max(a,b)$排序,然后dp
转移时,我们要分类讨论
如果$a_i < b_i$,因为要$max(a)<b_i$所以,$f[i-1][a[i]]$到$f[i-1][b[i]]$的最大值不会改变,直接加$val$就好,在$1-a[i-1]$的最大值变为了$a[i]$,用前面的最大值更新dp就好了。
如果$a_i > b_i$,因为要$max(a)<b_i$所以,$1-b[i]$的最大值变为$a[i]$,同上更新即可。
以上操作只需要维护一棵资磁区间加,单点修改,区间max线段树就好了。
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 const int N=2e5+10; 5 int cnt,lsh[N<<1]; 6 struct node{ 7 int a,b,w; 8 }p[N]; 9 int dp[N]; 10 bool cmp(node x,node y) 11 { 12 if(max(x.b,x.a)!=max(y.b,y.a)) 13 return max(x.b,x.a)<max(y.b,y.a); 14 else return x.b<y.b; 15 } 16 struct SegmentTree{ 17 int l,r,mx,lazy; 18 }tr[N<<2]; 19 void build(int p,int l,int r){ 20 tr[p].l=l,tr[p].r=r; 21 if(l==r){return ;} 22 int mid=(l+r)>>1; 23 build(p<<1,l,mid); 24 build(p<<1|1,mid+1,r); 25 } 26 void down(int p){ 27 if(!tr[p].lazy) return ; 28 tr[p<<1].lazy+=tr[p].lazy,tr[p<<1|1].lazy+=tr[p].lazy; 29 tr[p<<1].mx+=tr[p].lazy,tr[p<<1|1].mx+=tr[p].lazy; 30 tr[p].lazy=0; 31 } 32 void add(int p,int l,int r,int ll,int rr,int val){ 33 if(ll<=l&&r<=rr){ 34 tr[p].mx+=val; 35 tr[p].lazy+=val; 36 return ; 37 } 38 down(p); 39 int mid=(tr[p].l+tr[p].r)>>1; 40 if(ll<=mid) add(p<<1,l,mid,ll,rr,val); 41 if(rr>mid) add(p<<1|1,mid+1,r,ll,rr,val); 42 tr[p].mx=max(tr[p<<1].mx,tr[p<<1|1].mx); 43 } 44 int q_mx=0; 45 void ask(int p,int l,int r,int ll,int rr){ 46 if(ll<=l&&r<=rr){ 47 q_mx=max(q_mx,tr[p].mx); 48 return ; 49 } 50 down(p); 51 int mid=(tr[p].l+tr[p].r)>>1; 52 if(ll<=mid) ask(p<<1,l,mid,ll,rr); 53 if(rr>mid) ask(p<<1|1,mid+1,r,ll,rr); 54 } 55 void update(int p,int l,int r,int pos,int val){ 56 if(l==r){ 57 tr[p].mx=max(val,tr[p].mx); 58 return ; 59 } 60 down(p); 61 int mid=(tr[p].l+tr[p].r)>>1; 62 if(pos<=mid) update(p<<1,l,mid,pos,val); 63 else update(p<<1|1,mid+1,r,pos,val); 64 tr[p].mx=max(tr[p<<1].mx,tr[p<<1|1].mx); 65 } 66 signed main(){ 67 // freopen("2.in","r",stdin); 68 int n; 69 scanf("%lld",&n); 70 for(int i=1;i<=n;++i){ 71 scanf("%lld%lld%lld",&p[i].a,&p[i].b,&p[i].w); 72 } 73 sort(p+1,p+n+1,cmp); 74 for(int i=1;i<=n;++i) lsh[++cnt]=p[i].a,lsh[++cnt]=p[i].b; 75 sort(lsh+1,lsh+cnt+1); 76 int len=unique(lsh+1,lsh+cnt+1)-(lsh+1); 77 int maxn=0; 78 for(int i=1;i<=n;++i){ 79 p[i].a=lower_bound(lsh+1,lsh+len+1,p[i].a)-lsh; 80 p[i].b=lower_bound(lsh+1,lsh+len+1,p[i].b)-lsh; 81 maxn=max(maxn,p[i].a); 82 maxn=max(maxn,p[i].b); 83 } 84 build(1,1,maxn); 85 // cout<<"maxn=="<<maxn<<endl; 86 for(int i=1;i<=n;++i){ 87 if(p[i].a>p[i].b){ 88 q_mx=0; 89 ask(1,1,maxn,1,p[i].b); 90 // cout<<q_mx<<endl; 91 dp[i]=max(dp[i],q_mx+p[i].w); 92 } 93 else{ 94 q_mx=0; 95 ask(1,1,maxn,1,p[i].a); 96 // cout<<q_mx<<endl; 97 dp[i]=max(dp[i],q_mx+p[i].w); 98 add(1,1,maxn,p[i].a,p[i].b,p[i].w); 99 } 100 update(1,1,maxn,p[i].a,dp[i]); 101 } 102 printf("%lld\n",tr[1].mx); 103 }
T3 distance
感觉思路和meet in the middle 有些相似,不过也不太一样废话,这提重点思路就在于相当于从中间把两条路径连起来。
具体做法就是以所有特殊点为源点,跑一个多源点spfa,然后记录每个点,离最特殊点的距离,以及是那一个特殊点。跑完spfa后枚举所有边,如果两个端点不同就更新这两个端点的最近特殊点的答案。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 const int N=2e5+10; 5 int to[N<<1],nex[N<<1],edge[N<<1],fr[N<<1]; 6 int first[N]; 7 bool is[N]; 8 int sp[N],dis[N],v[N],ans[N]; 9 int tot; 10 void add(int a,int b,int c){ 11 to[++tot]=b,nex[tot]=first[a],first[a]=tot,edge[tot]=c,fr[tot]=a; 12 } 13 int sour[N]; 14 int n,m,p; 15 void spfa(){ 16 for(int i=1;i<=n;++i) dis[i]=10000000000000000; 17 queue<int> q; 18 for(int i=1;i<=p;++i) q.push(sp[i]),dis[sp[i]]=0,v[sp[i]]=1,sour[sp[i]]=sp[i]; 19 while(q.size()){ 20 int x=q.front();q.pop(); 21 v[x]=0; 22 for(int i=first[x];i;i=nex[i]){ 23 int y=to[i],z=edge[i]; 24 if(dis[y]>dis[x]+z){ 25 dis[y]=dis[x]+z; 26 sour[y]=sour[x]; 27 if(!v[y]) q.push(y),v[y]=1; 28 } 29 } 30 } 31 } 32 signed main(){ 33 //freopen("3.in","r",stdin); 34 scanf("%lld%lld%lld",&n,&m,&p); 35 for(int i=1;i<=n;++i) ans[i]=10000000000000000; 36 for(int i=1;i<=p;++i) {scanf("%lld",&sp[i]);is[sp[i]]=1;} 37 for(int i=1;i<=m;++i){ 38 int x,y,w; 39 scanf("%lld%lld%lld",&x,&y,&w); 40 add(x,y,w); 41 add(y,x,w); 42 } 43 spfa(); 44 // for(int i=1;i<=n;++i) cout<<"sour["<<i<<"]=="<<sour[i]<<" dis["<<i<<"]=="<<dis[i]<<endl; 45 for(int i=1;i<=tot;i+=2){ 46 if(sour[fr[i]]!=sour[to[i]]){ 47 // cout<<"g"<<fr[i]<<" "<<to[i]<<" "<<edge[i]<<endl; 48 ans[sour[fr[i]]]=min(ans[sour[fr[i]]],dis[fr[i]]+dis[to[i]]+edge[i]); 49 ans[sour[to[i]]]=min(ans[sour[to[i]]],dis[fr[i]]+dis[to[i]]+edge[i]); 50 51 } 52 } 53 for(int i=1;i<=p;++i) printf("%lld ",ans[sp[i]]); 54 } 55 /* 56 5 6 3 57 2 4 5 58 1 2 4 59 1 3 1 60 1 4 1 61 1 5 4 62 2 3 1 63 3 4 3 64 */