考得挺憋屈的。。。c++
先是搞了兩個半小時的 T1 後來發現假了,又沒多想跳了。。數組
而後一看 T2 這不是隊長快跑嘛。。。優化
先是根據本身的想法打了一遍(考完以後發現是對的。。)spa
而後回想了一下以前的題,不對呀,我記得有一個 if-else 的。code
接下來我就這麼改了,而後連樣例都過不去了。。。排序
後來 40min 碼完了兩個暴力(還有一個打錯了。。)ci
最後剩下了 30min 又去看 T1 了,推出來了半個正解 50ptsget
感受哪一個都有一點感受,會但不是徹底會。。string
擴展歐幾里德。。。it
發現當 x 爲 1 的時候能夠直接用擴展歐幾里德求出來。
也能夠進而求出其餘數的一組可行但不必定是最優的解。
假設已經有 \(ax'+by'=m\)
能夠推出: \(ax'+kab+by'-kab=m\)
所以能夠得出解集: \((x'+kb,y'-ka)\)
而後取最小的正值或者最大的負值就行了。
注意這裏最優的 x 不必定對應最優的 y 。
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } const int N=1e5+10; int n,a,b,g,lcm,ans,s[N],w[N]; struct Node { int x,y; }; int gcd(int x,int y) { if(!y) return x; return gcd(y,x%y); } Node exgcd(int a,int b) { if(!b) return (Node){1,0}; Node temp=exgcd(b,a%b); int x=temp.x,y=temp.y; return (Node){y,x-a/b*y}; } signed main() { n=read(); a=read(); b=read(); g=gcd(a,b); Node t=exgcd(a,b); t.x=abs(t.x); t.y=abs(t.y); for(int i=1;i<=n;i++) { s[i]=read(); if(s[i]<0) s[i]=-s[i]; if(s[i]%g){printf("-1");return 0;} } for(int i=1;i<=n;i++) s[i]/=g; a/=g; b/=g; lcm=a*b/g; for(int i=1;i<=n;i++) { int tmp1,tmp2,x,y; x=(s[i]*t.x/b)*b; y=(s[i]*t.y/a)*a; tmp1=min(abs(b+x-s[i]*t.x),abs(s[i]*t.x-x)); tmp2=min(abs(a+y-s[i]*t.y),abs(s[i]*t.y-y)); ans+=tmp1+tmp2; } printf("%lld",ans); return 0; }
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } const int N=1e5+10,INF=1e18; int n,a,b,g,lcm,ans,s[N],w[N]; struct Node { int x,y; }; int gcd(int x,int y) { if(!y) return x; return gcd(y,x%y); } Node exgcd(int a,int b) { if(!b) return (Node){1,0}; Node temp=exgcd(b,a%b); int x=temp.x,y=temp.y; return (Node){y,x-a/b*y}; } signed main() { n=read(); a=abs(read()); b=abs(read()); if(a>b) swap(a,b); g=gcd(a,b); Node t=exgcd(a,b); for(int i=1;i<=n;i++) { s[i]=read(); if(s[i]<0) s[i]=-s[i]; if(s[i]%g){printf("-1");return 0;} } a/=g; b/=g; for(int i=1;i<=n;i++) s[i]/=g; for(int i=1;i<=n;i++) { int num=s[i]/b; s[i]%=b; int x=s[i]*t.x; int y=s[i]*t.y+num; int temp=abs(x)+abs(y); if(x>0) { int tmp=abs(x)/b; x-=tmp*b; y+=tmp*a; temp=min(temp,abs(x)+abs(y)); temp=min(temp,abs(x-b)+abs(y+a)); } else { int tmp=abs(x)/b; x+=tmp*b; y-=tmp*a; temp=min(temp,abs(x)+abs(y)); temp=min(temp,abs(x+b)+abs(y-a)); } ans+=temp; } printf("%lld",ans); return 0; }
首先證實一下按照 \(a+b\) 從大到小排序的正確性。
假設 i 位於 j 以前,那麼必定是 \(a_i<b_j\) 而且 \(b_i>a_j\) 更優。
因此此時的 \(a_i+b_i<a_j+b_j\)(嚴格來說應該有等於的狀況)
而後對於相反的狀況也是差很少,剩下兩種狀況的排序的順序無所謂。。
接下來就是對於 \([1,min(a_i,b_i)]\) 以及 \([a_i+1,b_i]\) 這兩個區間的更新了。
設 DP數組 \(f_{i,j}\) 表示排序以後的序列選前 i 個數,最大的 a 爲 j 的最大值。。
發現這個能夠線段樹優化,單點修改,區間修改,區間查詢就行了。
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } const int N=1e5+10,M=3e3+10; int n,ans,f[M][M<<1]; int cnt,lsh[N]; struct Node { int a,b,w; }s[N]; bool comp(Node x,Node y) { return x.a+x.b<y.a+y.b; } signed main() { n=read(); for(int i=1;i<=n;i++) { s[i].a=read(); s[i].b=read(); s[i].w=read(); lsh[++cnt]=s[i].a; lsh[++cnt]=s[i].b; } sort(s+1,s+n+1,comp); sort(lsh+1,lsh+cnt+1); cnt=unique(lsh+1,lsh+cnt+1)-lsh-1; for(int i=1;i<=n;i++) { s[i].a=lower_bound(lsh+1,lsh+cnt+1,s[i].a)-lsh; s[i].b=lower_bound(lsh+1,lsh+cnt+1,s[i].b)-lsh; } for(int i=1;i<=n;i++) { for(int j=1;j<=cnt;j++) f[i][j]=f[i-1][j]; for(int j=1;j<=min(s[i].a,s[i].b);j++) f[i][s[i].a]=max(f[i][s[i].a],f[i-1][j]+s[i].w); for(int j=s[i].a+1;j<=s[i].b;j++) f[i][j]=max(f[i][j],f[i-1][j]+s[i].w); } for(int i=1;i<=n;i++) for(int j=1;j<=cnt;j++) ans=max(ans,f[i][j]); printf("%lld",ans); return 0; }
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl #define ls x<<1 #define rs x<<1|1 using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } const int N=2e5+10; int n,ans,tre[N<<2],laz[N<<2]; int cnt,lsh[N]; struct Node { int a,b,w; }s[N]; bool comp(Node x,Node y) { return x.a+x.b<y.a+y.b; } void push_up(int x) { tre[x]=max(tre[ls],tre[rs]); } void push_down(int x) { if(!laz[x]) return ; tre[ls]+=laz[x]; tre[rs]+=laz[x]; laz[ls]+=laz[x]; laz[rs]+=laz[x]; laz[x]=0; } int query(int x,int l,int r,int L,int R) { if(L<=l&&r<=R) return tre[x]; push_down(x); int mid=(l+r)>>1,ans1=0,ans2=0; if(L<=mid) ans1=query(ls,l,mid,L,R); if(R>mid) ans2=query(rs,mid+1,r,L,R); push_up(x); return max(ans1,ans2); } void update(int x,int l,int r,int L,int R,int num) { if(L<=l&&r<=R) { tre[x]+=num; laz[x]+=num; return ; } push_down(x); int mid=(l+r)>>1; if(L<=mid) update(ls,l,mid,L,R,num); if(R>mid) update(rs,mid+1,r,L,R,num); push_up(x); } void insert(int x,int l,int r,int pos,int num) { if(l==r) { tre[x]=max(tre[x],num); return ; } push_down(x); int mid=(l+r)>>1; if(pos<=mid) insert(ls,l,mid,pos,num); else insert(rs,mid+1,r,pos,num); push_up(x); } signed main() { n=read(); for(int i=1;i<=n;i++) { s[i].a=read(); s[i].b=read(); s[i].w=read(); lsh[++cnt]=s[i].a; lsh[++cnt]=s[i].b; } sort(s+1,s+n+1,comp); sort(lsh+1,lsh+cnt+1); cnt=unique(lsh+1,lsh+cnt+1)-lsh-1; for(int i=1;i<=n;i++) { s[i].a=lower_bound(lsh+1,lsh+cnt+1,s[i].a)-lsh; s[i].b=lower_bound(lsh+1,lsh+cnt+1,s[i].b)-lsh; } for(int i=1;i<=n;i++) { if(s[i].b>s[i].a) update(1,1,cnt,s[i].a+1,s[i].b,s[i].w); insert(1,1,cnt,s[i].a,query(1,1,cnt,1,min(s[i].a,s[i].b))+s[i].w); } printf("%lld",tre[1]); return 0; }
思路極簡。。
多源最短路,同時記錄每個點的最小值來自於哪個特殊點,跑一下 Dij
接下來枚舉枚舉每一條邊,用這條邊的兩個端點的兩距離個最小值與邊權之和更新兩個特殊點的答案就行了。
而後就沒有而後了。。
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } const int N=2e5+10; int n,m,p,s[N],dis[N],ans[N],pre[N]; bool vis[N]; int tot=1,head[N],nxt[N<<1],ver[N<<1],edge[N<<1]; struct Road { int l,r,val; }pat[N]; priority_queue<pair<int,int> > q; void add(int x,int y,int val) { ver[++tot]=y; edge[tot]=val; nxt[tot]=head[x]; head[x]=tot; } void Dij() { memset(dis,0x3f,sizeof(dis)); for(int i=1;i<=p;i++) { pre[s[i]]=s[i]; dis[s[i]]=0; q.push(make_pair(0,s[i])); } while(!q.empty()) { int x=q.top().second; q.pop(); if(vis[x]) continue; vis[x]=true; for(int i=head[x];i;i=nxt[i]) { int to=ver[i]; if(dis[x]+edge[i]<dis[to]) { pre[to]=pre[x]; dis[to]=dis[x]+edge[i]; q.push(make_pair(-dis[to],to)); } } } } signed main() { n=read(); m=read(); p=read(); for(int i=1;i<=p;i++) s[i]=read(); memset(ans,0x3f,sizeof(ans)); for(int i=1,x,y,val;i<=m;i++) { pat[i].l=x=read(); pat[i].r=y=read(); pat[i].val=val=read(); add(x,y,val); add(y,x,val); } Dij(); for(int i=1;i<=m;i++) { int x=pat[i].l,y=pat[i].r; if(pre[x]==pre[y]) continue; ans[pre[x]]=min(ans[pre[x]],dis[x]+dis[y]+pat[i].val); ans[pre[y]]=min(ans[pre[y]],dis[x]+dis[y]+pat[i].val); } for(int i=1;i<=p;i++) printf("%lld ",ans[s[i]]); return 0; }
思路來自 AaMuXiiiiii 。。
記錄每個 \(\$\) 的位置以及所表明的值。
分別枚舉全部的值,而後逆推回去看真話的數量是否一直。
若是都不成立的話,在判斷一下說的都是假話的話是否可行。
若是沒有 \(\$\) 的話直接分類討論第一個是不是真話是否合法就行了。
代碼實現細節較多,數組清零不能夠 memset 會 TLE
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } const int N=1e5+10; int T,n,s[N],t[N],pre[N][2],ans[N][2]; map<int,bool> b; vector<int> pos,v; string opt; void clear() { b.clear(); for(int i=0;i<=n;i++) pre[i][0]=pre[i][1]=ans[i][0]=ans[i][1]=0; vector<int>().swap(pos); vector<int>().swap(v); } void solve() { n=read(); int flag=0,maxn=0; for(int i=1;i<=n;i++) { cin>>opt; if(opt[0]=='$') scanf("%lld",&s[i]),flag=i; if(opt[0]=='+') s[i]=-1; if(opt[0]=='-') s[i]=-2; maxn=max(maxn,s[i]); } for(int i=1;i<=n-flag;i++) t[i]=s[i+flag]; for(int i=1;i<=flag;i++) t[i+n-flag]=s[i]; memcpy(s,t,sizeof(t)); if(flag) { for(int i=1;i<=n;i++) if(s[i]>=0) { pos.push_back(i); if(!b[s[i]]){b[s[i]]=true;v.push_back(s[i]);} } for(int i=n;i>=1;i--) { if(s[i]<0) continue; int las=true; pre[i][1]=1; for(int j=i-1;j>=1;j--) { if(s[j]>=0) break; if(s[j]==-1) { if(las) pre[i][1]++,las=true; else las=false; } else { if(!las) pre[i][1]++,las=true; else las=false; } } las=false; for(int j=i-1;j>=1;j--) { if(s[j]>=0) break; if(s[j]==-1) { if(las) pre[i][0]++,las=true; else las=false; } else { if(!las) pre[i][0]++,las=true; else las=false; } } } int all=0; for(int i=0;i<pos.size();i++) { ans[s[pos[i]]][0]+=pre[pos[i]][0]; ans[s[pos[i]]][1]+=pre[pos[i]][1]; } for(int i=0;i<v.size();i++) all+=ans[v[i]][0]; for(int i=0;i<v.size();i++) if(ans[v[i]][1]-ans[v[i]][0]+all==v[i]) { printf("consistent\n"); clear(); return ; } int sum=0; for(int i=0;i<pos.size();i++) sum+=pre[pos[i]][0]; for(int i=0;i<v.size();i++) if(sum==v[i]) { printf("inconsistent\n"); clear(); return ; } printf("consistent\n"); clear(); return ; } bool las=true; for(int i=n-1;i>=1;i--) if(s[i]==-2) las^=1; if(las&&s[n]==-1) { printf("consistent\n"); return ; } if(!las&&s[n]==-2) { printf("consistent\n"); return ; } las=false; for(int i=n-1;i>=1;i--) if(s[i]==-2) las^=1; if(las&&s[n]==-2) { printf("consistent\n"); return ; } if(!las&&s[n]==-1) { printf("consistent\n"); return ; } printf("inconsistent\n"); } signed main() { T=read(); while(T--) solve(); return 0; }