Description
NOI2011 在吉林大學開始啦!為了迎接來自全國各地最優秀的信息學選手,吉林大學決定舉辦兩場盛大的 NOI 嘉年華活動,分在兩個不同的地點舉辦。每個嘉年華可能包含很多個活動,而每個活動只能在一個嘉年華中舉辦。
現在嘉年華活動的組織者小安一共收到了 n個活動的舉辦申請,其中第 i 個活動的起始時間為 Si,活動的持續時間為Ti。這些活動都可以安排到任意一個嘉年華的會場,也可以不安排。
小安通過廣泛的調查發現,如果某個時刻,兩個嘉年華會場同時有活動在進行(不包括活動的開始瞬間和結束瞬間),那么有的選手就會糾結于到底去哪個會場,從而變得不開心。所以,為了避免這樣不開心的事情發生,小安要求不能有兩個活動在兩個會場同時進行(同一會場內的活動可以任意進行)。
另外,可以想象,如果某一個嘉年華會場的活動太少,那么這個嘉年華的吸引力就會不足,容易導致場面冷清。所以小安希望通過合理的安排,使得活動相對較少的嘉年華的活動數量最大。
此外,有一些活動非常有意義,小安希望能舉辦,他希望知道,如果第i 個活動必須舉辦(可以安排在兩場嘉年華中的任何一個),活動相對較少的嘉年華的活動數量的最大值。
Solution
正解:DP+單調性優化
第一問非常簡單,有兩個嘉年華,所以要固定一個,設 \(f[i][j]\) 表示目前在 \(i\) 這個活動的右端點上,選擇了\(j\)個放入第一個嘉年華,第二個嘉年華的最大活動數量
轉移有兩種:枚舉 \(k<i\),\(k\)到\(i\)之間活動的要不給第一個嘉年華要不給第二個,所以預處理一個 \(c[i][j]\) 表示 \(i\)到\(j\)時間段內的活動數量,就可以 \(O(1)\) 轉移了
第二問可以直接用第一問的DP數組,同理再求一個后綴的 \(f\) 數組,設為 \(g\).
那么如果是固定時間在 \(i,j\) 之間的活動的話,那么就是從 \(f[i-1][a]+g[j+1][b]+c[i][j]\) 中決策了,枚舉\(a,b\),討論\(c[i][j]\)分給第一個嘉年華,分給第二個嘉年華即可.
這樣做的話是 \(O(n^5)\),所以只需要離線詢問,設 \(dp[i][j]\) 表示上面說的:固定時間在 \(i,j\) 之間的活動,兩個嘉年華中的最小值的最大值為多少
回答詢問就可以 O\((n^2)\) 了.
注意:預處理 \(dp\) 數組是 \(O(n^4)\),但是限制上界的話,暴力是可以過的.
正解是 \(O(n^3)\) 預處理的,利用一個單調性:
假設枚舉\(a\)為左邊分給第一個嘉年華的活動數量,\(b\)為右邊分給第一個嘉年華的
那么 \(a\)增大,\(b\)只能減小,所以可以單調指針掃描 \(b\),復雜度就降下來了.
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=405;
int n,B[N],num=0,c[N][N],f[N][N],g[N][N],dp[N][N],m;
struct sub{int l,r;}e[N];
void work()
{scanf("%d",&n);m=n;for(int i=1;i<=n;i++){scanf("%d%d",&e[i].l,&e[i].r);e[i].r=e[i].l+e[i].r-1;B[++num]=e[i].l;B[++num]=e[i].r;}sort(B+1,B+num+1);num=unique(B+1,B+num+1)-B-1;for(int i=1;i<=m;i++){e[i].l=lower_bound(B+1,B+num+1,e[i].l)-B;e[i].r=lower_bound(B+1,B+num+1,e[i].r)-B;for(int j=e[i].l;j>=1;j--)for(int k=e[i].r;k<=num;k++)c[j][k]++;}memset(f,-127/3,sizeof(f));memset(g,-127/3,sizeof(g));n=num;f[0][0]=0;for(int i=1;i<=n;i++)for(int j=0;j<=c[1][i];j++){f[i][j]=f[i-1][j];for(int k=0;k<i;k++){f[i][j]=Max(f[i][j],f[k][j]+c[k+1][i]);if(j>=c[k+1][i])f[i][j]=Max(f[i][j],f[k][j-c[k+1][i]]);}}g[n+1][0]=0;for(int i=n;i>=1;i--)for(int j=0;j<=c[i][n];j++){g[i][j]=g[i+1][j];for(int k=i+1;k<=n+1;k++){g[i][j]=Max(g[i][j],g[k][j]+c[i][k-1]);if(j>=c[i][k-1])g[i][j]=Max(g[i][j],g[k][j-c[i][k-1]]);}}int ans=0;for(int i=1;i<=n;i++)for(int j=0;j<=i;j++)ans=max(ans,min(f[i][j],j));printf("%d\n",ans);int v1,v2,b;for(int i=1;i<=n;i++)for(int j=i;j<=n;j++){b=c[j+1][n];for(RG int a=0;a<=c[1][i-1];a++){for(;b>=0;b--){v1=min(a+b,f[i-1][a]+g[j+1][b]+c[i][j]);v1=max(v1,min(a+b+c[i][j],f[i-1][a]+g[j+1][b]));v2=min(a+b-1,f[i-1][a]+g[j+1][b-1]+c[i][j]);v2=max(v2,min(a+b+c[i][j]-1,f[i-1][a]+g[j+1][b-1]));if(v1>v2)break;}dp[i][j]=Max(dp[i][j],v1);}}for(int i=1;i<=m;i++){ans=0;for(int j=e[i].l;j>=1;j--)for(int k=e[i].r;k<=n;k++)ans=max(ans,dp[j][k]);printf("%d\n",ans);}
}int main()
{freopen("pp.in","r",stdin);freopen("pp.out","w",stdout);work();return 0;
}
轉載于:https://www.cnblogs.com/Yuzao/p/8149094.html
總結
以上是生活随笔為你收集整理的bzoj 2436: [Noi2011]Noi嘉年华的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。