[USACO 2018 Open Gold] Tutorial
阿新 • • 發佈:2018-09-09
push_back index.php 算法 urn turn 分答 lse ora pac
Link:
傳送門
A:
對於每一條分割線,設本不應在其左側的個數為$x$
重點要發現每次一來一回的操作恰好會將一對分別應在左/右側的一個數從右/左移過去
這樣就轉直接用樹狀數組求出最大的$x$即可
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef long long ll; typedef pair<int,int> P; const int MAXN=1e5+10; //res至少為1 int n,bit[MAXN],res=1;P dat[MAXN];Problem Avoid Update(int x) {while(x<=n) bit[x]++,x+=x&(-x);} int Query(int x) { int ret=0; while(x) ret+=bit[x],x-=x&(-x); return ret; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&dat[i].X),dat[i].Y=i; sort(dat+1,dat+n+1); for(int i=1;i<n;i++) Update(dat[i].Y),res=max(res,i-Query(i)); printf("%d",res); return 0; }
冒泡排序相關題多考慮數與數相對位置以及每個數應在的位置
B:
發現答案具有單調性後首先想到二分答案或是直接枚舉
由於不用重復建圖,直接枚舉看起來能更快一些,不過好像並不能在線$O(logn)$判斷是否出現了環……
(按照楊主力的指示還是在google上搜到了論文,不過並不打算學,傳送門)
剩下就是優先隊列套拓撲排序就行了
#include <bits/stdc++.h> usingProblem Bnamespace std; #define X first #define Y second #define pb push_back typedef long long ll; typedef pair<int,int> P; typedef double db; const int MAXN=1e5+10; vector<int> dat[MAXN]; struct edge{int nxt,to;}e[MAXN<<2]; int n,m,num,x,head[MAXN],res[MAXN],in[MAXN],tot; void add_edge(int x,int y) {e[++tot].nxt=head[x];e[tot].to=y;head[x]=tot;} void build(int x) { tot=0; memset(in,0,sizeof(in)); memset(head,0,sizeof(head)); for(int i=1;i<=x;i++) for(int j=0;j<dat[i].size()-1;j++) add_edge(dat[i][j],dat[i][j+1]),in[dat[i][j+1]]++; } bool check() { int sz=0; priority_queue<int,vector<int>,greater<int> > q; for(int i=1;i<=n;i++) if(!in[i]) q.push(i); while(!q.empty()) { int t=q.top();q.pop(); res[++sz]=t; for(int i=head[t];i;i=e[i].nxt) { in[e[i].to]--; if(!in[e[i].to]) q.push(e[i].to); } } return (sz==n); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d",&num); for(int j=1;j<=num;j++) scanf("%d",&x),dat[i].pb(x); } int l=1,r=m; while(l<=r) { int mid=(l+r)>>1; build(mid); if(check()) l=mid+1; else r=mid-1; } build(r);check(); for(int i=1;i<=n;i++) printf("%d ",res[i]); return 0; }
C:
明顯0/1分數規劃,每次$dp$找當前重量大於等於$m$的最大權值和即可
一篇很好的博客,圖解0/1分數規劃:傳送門
順便學了下$Dinkelbach$算法,發現就是每次找到最大值時直接跳到該方案的零點處
直到該方案的零點為當前$x$下的最大值時停止即可
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef long long ll; typedef pair<int,int> P; typedef double db; const int MAXN=1e5+10; int n,m,w[MAXN],t[MAXN]; db l,r,a[MAXN],dp[MAXN]; bool check(db x) { for(int i=1;i<=n;i++) a[i]=t[i]-1.0*x*w[i]; for(int i=1;i<=m;i++) dp[i]=-1e9; for(int i=1;i<=n;i++) for(int j=m;j>=0;j--) dp[min(m,j+w[i])]=max(dp[min(m,j+w[i])],dp[j]+a[i]); return dp[m]>=0; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&t[i]),r+=t[i]; while(fabs(l-r)>1e-6) { db mid=(l+r)/2.0; if(check(mid)) l=mid; else r=mid; } printf("%d",int((l+r)*500)); return 0; }Problem C
[USACO 2018 Open Gold] Tutorial