UOJ#29. 【IOI2014】Holiday

传送门

分析

真不明白这道题为啥要是交互题,难道是为了普及交互题这种题型???嘤嘤嘤

见到这个题的第一个反应:网络流?建一个流量是d的边??

第二个反映:如果对于起点为0的情况可以dp,所以只要把左半段接到右半段后面或者右半段接到左半段后面然后通过一些奇怪的东西转移即可,复杂度O(nd)??

第三个反映:我在扯淡!

正解:

我们知道路线一定是先向一边走一段再折返回来走另一边(也可能不回来了)

于是对于一段路径[le,ri]我们可以先把一共要走多远计算出来,剩下k天就是在主席树中寻找这一段前k大数的和

然后我们又得到一个非常明显的性质:

  我们设f(x)为向一个方向不回头走x天的最大值,g(x)为回头的最大值

  则对于任意i<j都有f(i)<=f(j),g(i)<=g(j)

这玩意有个学名叫决策单调性

于是我们可以考虑二分右边的点mid,然后对于二分到的点枚举左边的点求出它的最大值

设此时它对应的左边的点为pl则当右边点小于mid时左边对应的点一定不大于pl,右边点大于mid时反之

代码

#include"holiday.h"
#include<bits/stdc++.h>
using namespace std;
#define li long long
int a[100100],v[100100],s,D,n,m,root[100100],cnt;
li Ans;
struct node {
    int le,ri,sum;
    li ans;
};
node d[4400000];
inline void update(int le,int ri,int &x,int y,int k){
    d[++cnt]=d[y];
    x=cnt;
    d[x].sum++;
    d[x].ans+=v[k];
    if(le==ri)return;
    int mid=(le+ri)>>1;
    if(k<=mid)update(le,mid,d[x].le,d[y].le,k);
      else update(mid+1,ri,d[x].ri,d[y].ri,k);
}
inline li q(int le,int ri,int x,int y,int k){
    if(le==ri)return (li)v[le]*min(k,d[y].sum-d[x].sum);
    int mid=(le+ri)>>1,tot=d[d[y].ri].sum-d[d[x].ri].sum;
    if(tot>=k)return q(mid+1,ri,d[x].ri,d[y].ri,k);
      else return d[d[y].ri].ans-d[d[x].ri].ans+q(le,mid,d[x].le,d[y].le,k-tot);
}
inline void solve(int L,int R,int le,int ri){
    li res=0,tot=0;
    int i,j,k,pl=R,mid=(le+ri)>>1;
    for(i=L;i<=R;i++){
      k=D-(mid-i)-min(mid-s,s-i);
      if(k){
          tot=q(1,m,root[i-1],root[mid],k);
          if(tot>res){
            res=tot;
            pl=i;
          }
      }
    }
    Ans=max(Ans,res);
    if(mid>le)solve(L,pl,le,mid-1);
    if(mid<ri)solve(pl,R,mid+1,ri);
}
li findMaxAttraction(int nn, int ss, int dd, int aa[]){
    int i,j,k;
    n=nn,s=ss,D=dd;
    s++;
    for(i=1;i<=n;i++)a[i]=aa[i-1],v[i]=aa[i-1];
    sort(v+1,v+n+1);
    m=unique(v+1,v+n+1)-v-1;
    for(i=1;i<=n;i++)
      update(1,m,root[i],root[i-1],lower_bound(v+1,v+m+1,a[i])-v);
    solve(1,s,s,n);
    return Ans;
}

猜你喜欢

转载自www.cnblogs.com/yzxverygood/p/10368717.html