T1.manage [LUOGU]P2920
按结束时间排序,贪心即可。
T2.software [BZOJ] 2427
每个软件只依赖一个软件,和《软件包管理器》的描述非常像,但这里没有特别声明,是可以构成环的。
一个环上只能所有软件同时都安装或者不安装,所以考虑把环缩成一个点。
Tarjan缩点后,就是一个树上背包问题了。
我们可以把子树泛化成物品,本质上是一个分组背包。
代码见附录①。
T3.rebuild [BZOJ] 2957
首先将楼房属性(位置x,高度y)转化为斜率y/x,我们要求的是一个斜率递增序列的长度。
用线段树解决,考虑维护区间最长序列长度len (是不是二维偏序序列长度都能用线段树?)
查询操作,就是len[root]。
修改操作,就是单点修改的问题,在回溯时,考虑如何进行pushup操作。
记当前节点为cur,左右儿子分别为ls,rs
法1:记录区间斜率最大值mx和最小值mn,进行分类讨论。
1. mx[ls]<mn[rs],可以直接合并两个区间。
2. mn[ls]>mx[rs],右区间全部被遮挡,只留左区间。
3. otherwise,在右区间二分mx[ls]的位置pos,合并左区间和右区间[pos,r]部分。
法2:记录区间斜率最大值mx
右区间的最大值一定有机会成为最大值,mx[cur]=max(mx[ls],mx[rs])
对于len,直接将mx[ls]在右区间进行二分即可。
一点细节,二分中,当w<mx[cur]时返回query(ls,l,mid,w)+len[cur]-len[ls]
这里一定是len[cur]-len[ls],它和len[rs]不等价!
#include<iostream> #include<algorithm> #include<cstdio> using namespace std; const int MAXN=100005; inline int rd(){ int ret=0,f=1;char c; while(c=getchar(),!isdigit(c))f=c=='-'?-1:1; while(isdigit(c))ret=ret*10+c-'0',c=getchar(); return ret*f; } struct Node{ int lon,tim; }nodes[MAXN]; bool cmp(const Node &x,const Node &y){ return x.tim<y.tim; } int n; int main(){ n=rd(); for(int i=1;i<=n;i++){ nodes[i].lon = rd(); nodes[i].tim = rd(); } sort(nodes+1,nodes+1+n,cmp); long long sum,mn=(1<<29),v; for(int i=1;i<=n;i++){ sum+=nodes[i].lon; v=nodes[i].tim-sum; if(v<0) return puts("-1"),0; mn=min(mn,v); } printf("%lld",mn); return 0; }
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int MAXN=128; int n,m; int cost[MAXN],scost[MAXN]; int val[MAXN],sval[MAXN]; inline int rd(){ int ret=0,f=1;char c; while(c=getchar(),!isdigit(c))f=c=='-'?-1:1; while(isdigit(c))ret=ret*10+c-'0',c=getchar(); return ret*f; } struct Edge{ int to[MAXN],nxt[MAXN]; int ecnt,head[MAXN]; #define M(a) memset(a,0,sizeof(a)) Edge(){M(to);M(nxt);M(head);ecnt=0;} #undef M void add(int x,int y){ to[++ecnt]=y; nxt[ecnt]=head[x]; head[x]=ecnt; } }e1,e2; int ind[MAXN]; int scc[MAXN],cnt; int dfn[MAXN],low[MAXN],tim; int sta[MAXN],ins[MAXN],top; void tarjan(int x){ dfn[x]=low[x]=++tim; ins[sta[++top]=x]=1; for(int i=e1.head[x];i;i=e1.nxt[i]){ int v=e1.to[i]; if(!dfn[v]) {tarjan(v);low[x]=min(low[x],low[v]);} else if(ins[v]) low[x]=min(low[x],dfn[v]); } if(dfn[x]==low[x]){ int elm=-1;cnt++; while(elm!=x){ ins[elm=sta[top--]]=0; scc[elm]=cnt; scost[cnt]+=cost[elm]; sval[cnt]+=val[elm]; } } } int f[128][512]; void dfs(int x){ for(int i=e2.head[x];i;i=e2.nxt[i]){ int v=e2.to[i]; dfs(v); for(int j=m-scost[x];j>=0;j--) for(int k=0;k<=j;k++) f[x][j]=max(f[x][j],f[x][k]+f[v][j-k]); } for(int i=m;i>=0;i--) if(i>=scost[x]) f[x][i]=f[x][i-scost[x]]+sval[x]; else f[x][i]=0; } int main(){ n=rd();m=rd(); for(int i=1;i<=n;i++) cost[i]=rd(); for(int i=1;i<=n;i++) val[i]=rd(); for(int i=1;i<=n;i++) e1.add(rd(),i); for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;i++) for(int j=e1.head[i];j;j=e1.nxt[j]){ int v=e1.to[j]; if(scc[v]!=scc[i]) e2.add(scc[i],scc[v]),ind[scc[v]]++; } for(int i=1;i<=cnt;i++) if(!ind[i]) e2.add(cnt+1,i); dfs(cnt+1); cout<<f[cnt+1][m]; return 0; }
#include<iostream> #include<cstdio> using namespace std; inline int rd(){ int ret=0,f=1;char c; while(c=getchar(),!isdigit(c))f=c=='-'?-1:1; while(isdigit(c))ret=ret*10+c-'0',c=getchar(); return ret*f; } const int MAXN=100005; int n,m; #define mid ((l+r)>>1) #define ls (cur<<1) #define rs (cur<<1|1) double mx[MAXN<<2],mn[MAXN<<2]; int len[MAXN<<2]; int query(int cur,int l,int r,double w){ if(l==r) return mx[cur]>w; if(mx[ls]<=w) return query(rs,mid+1,r,w); return query(ls,l,mid,w)+len[cur]-len[ls]; } void pushup(int cur,int l,int r){ mx[cur]=max(mx[ls],mx[rs]); len[cur]=len[ls]+query(rs,mid+1,r,mx[ls]); } void update(int x,int cur,int l,int r,double w){ if(l==r){mx[cur]=w;len[cur]=1;return;} if(x<=mid) update(x,ls,l,mid,w); if(mid <x) update(x,rs,mid+1,r,w); pushup(cur,l,r); } int main(){ n=rd();m=rd(); int x,y; for(int i=1;i<=m;i++){ x=rd();y=rd(); double d=1.0*y/x; update(x,1,1,n,d); printf("%d ",len[1]); } return 0; }