NOI2017 day2T2 vegetables 题解(伪)

来源:互联网 发布:若星汉天空 知乎 编辑:程序博客网 时间:2024/06/14 18:13

(同步赛的时候day2的T2T3都爆0了...我这么弱还是回家搞文化吧)

这题首先注意题意.我们发现题意实际上是说,有很多份能买一次的蔬菜,它们有一个价值和一个变质时间.第一次卖的每种蔬菜可以认为是最后变质的(我们肯定希望更有价值的更好卖)

接下来把所有蔬菜按价值从大到小放在一个优先队列里(或者说直接排序也可以),对于每份蔬菜,如果能刚好在它变质那一天卖掉,就在它变质那一天卖掉;否则看变质前一天...以此类推.正确性的话...放弃一份价值高的蔬菜最多能多卖出一份价值低的蔬菜,肯定是不划算的.更晚卖肯定比更早卖优.

用上面的方法加上一些优化大概有80分.

考虑先算出p最大的那一天的收益.把每种蔬菜放进优先队列里(第一次卖的单独再算一种),接下来从这种蔬菜最后变质的一份变质的那一天开始,如果那一天能卖完就卖完,否则就会有剩余到前一天,以此一直推到第0天.注意两个保证复杂度的细节:用并查集快速处理连续的不能再卖东西的几天;如果一种蔬菜不会变质,而且已经没有剩余,则要及早跳出循环.

上面这个过程的复杂度大约是(2nlogn+m*max(p)*(并查集复杂度)),大约是1e6或者1e7级别的.

接下来计算之前每一天的收益.把所有可以被卖掉的蔬菜放进一个优先队列里.每当减少一天时,就把价值最小的m份蔬菜的价值减掉.这样就处理出了每一天的收益.复杂度(m*max(p)*log(m*max(p))),大约是1e7级别.(这个过程可以看做是把最后一天的蔬菜替换掉了之前价值最小的蔬菜的蔬菜)

至此,这个问题就被解决了.这只是我的方法,如果有错误欢迎指出QAQ.

(赛场上看错题了真是***)

具体细节见代码.(顺便,%%%dy)

#include<cstdio>#include<algorithm>#include<cstdlib>#include<vector>#include<queue>using namespace std;const int maxm=100000;//maxm是最大天数int Min(int x,int y){return x<y?x:y;}int Max(int x,int y){return x>y?x:y;}struct Node{int a,s,c,x;void readl(){scanf("%d%d%d%d",&a,&s,&c,&x);}}b[100100];struct node{int a,c,x;}d[200100];bool cmp(const node &A,const node &B){return A.a>B.a;}int g[1001000];int num,cnt;int m;int f[100100];int dis[100100];long long dp[100100];int getfa(int x){//用一种鬼畜方式写的并查集if(dis[f[x]]<m) return f[x];return f[x]=getfa(f[x]-1);}int main(){freopen("vegetables.in","r",stdin);freopen("vegetables.out","w",stdout);int n,T,cas;scanf("%d%d%d",&n,&m,&T);for(int i=1;i<=n;i++)b[i].readl();for(int i=1;i<=n;i++){if(b[i].c>1)d[++num]=(node){b[i].a,b[i].c-1,b[i].x};if(b[i].x==0)d[++num]=(node){b[i].a+b[i].s,maxm,-1};//把第一次卖的单独考虑,注意特殊处理x=0的情况elsed[++num]=(node){b[i].a+b[i].s,(b[i].c-1)/b[i].x+1,-1};}sort(d+1,d+num+1,cmp);for(int i=1;i<=100000;i++)f[i]=i;node st;int ps,s,t,lef;long long ans=0;for(int i=1;i<=num;i++){st=d[i];if(st.x==-1){t=getfa(Min(st.c,maxm));if(t<=0) continue;dis[t]++,g[++cnt]=st.a;ans=ans+st.a;}else{t=(st.x==0?maxm:Min(maxm,(st.c-1)/st.x+1));//最后变质的一天,同样注意x==0的情况lef=st.c-st.x*(t-1);//当前剩余可以卖的蔬菜数量while(t>0){while(lef>0&&dis[t]<m){lef--;dis[t]++,g[++cnt]=st.a;ans=ans+st.a;}if(lef==0&&st.x==0) break;//如果不加这句话,就会导致进入大循环次数过多s=getfa(t-1);lef+=(t-s)*st.x;t=s;}}}dp[maxm]=ans;cnt=maxm*m;sort(g+1,g+cnt+1);//g是所有卖出去了的蔬菜int ilem=1;for(int i=maxm-1;i>=1;i--){for(int j=1;j<=m;j++){ans-=g[ilem];ilem++;}dp[i]=ans;//dp是答案}for(cas=1;cas<=T;cas++){scanf("%d",&ps);printf("%lld\n",dp[ps]);}return 0;}


原创粉丝点击