标签:splay alt 最大的 blog lap 好的 one tps csdn
对于每一条分割线,设本不应在其左侧的个数为$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]; void 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; }
冒泡排序相关题多考虑数与数相对位置以及每个数应在的位置
发现答案具有单调性后首先想到二分答案或是直接枚举
由于不用重复建图,直接枚举看起来能更快一些,不过好像并不能在线$O(logn)$判断是否出现了环……
(按照杨主力的指示还是在google上搜到了论文,不过并不打算学,传送门)
剩下就是优先队列套拓扑排序就行了
#include <bits/stdc++.h> using namespace 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; }
明显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; }
[USACO 2018 Open Gold] Tutorial
标签:splay alt 最大的 blog lap 好的 one tps csdn
原文地址:https://www.cnblogs.com/newera/p/9603979.html