COCI2017-2018-2 San
题意
有(n leq 40)个节点,每个节点有权值(H leq 1e9)和贡献(v leq 1e9),从任意一个点可以向右跳到一个权值不小于它的节点,并获得该点贡献
可以从任意一个点开始,任意一个点结束
看数据范围可知此题是典型的折半搜索
方法:离散化高度后将数据分为左右两部分,分别DFS出两边的合法状态,然后排序考虑合并
左半部从大到小排序,右半部从小到大排序,统计右半部的各种高度前缀和
双指针从左往右扫,统计合法的方案数
#include"cstdio"
#include"cstring"
#include"iostream"
#include"algorithm"
#include"map"
using namespace std;
const int MAXN=45;
int n,np,mid,o[MAXN];
long long c,ans;
int H[MAXN],v[MAXN],cnt[2];
int lis[MAXN];
map<int,int> p;
struct rpg{
long long v;
int h;
}a[2][1<<20];
void dfs1(long long val,int h,int tp)
{
a[0][++cnt[0]]=(rpg){val,h};
if(tp==mid) return;
for(int i=tp+1;i<=mid;++i) if(H[i]>=h) dfs1(val+v[i],H[i],i);
return;
}
void dfs2(long long val,int lh,int h,int tp)
{
a[1][++cnt[1]]=(rpg){val,lh};
if(tp==n) return;
for(int i=tp+1;i<=n;++i) if(H[i]>=h) dfs2(val+v[i],lh?lh:H[i],H[i],i);
return;
}
bool cmp1(rpg a,rpg b){return a.v>b.v;}
bool cmp2(rpg a,rpg b){return a.v<b.v;}
int main()
{
scanf("%d%lld",&n,&c);
for(int i=1;i<=n;++i) scanf("%d%d",&H[i],&v[i]),o[i]=H[i];
sort(o+1,o+n+1);for(int i=1;i<=n;++i) p[o[i]]=p[o[i-1]]+(o[i]>o[i-1]);
for(int i=1;i<=n;++i) H[i]=p[H[i]];
mid=1+n>>1;dfs1(0,0,0);dfs2(0,0,0,mid);
sort(a[0]+1,a[0]+cnt[0]+1,cmp1);
sort(a[1]+1,a[1]+cnt[1]+1,cmp2);
a[1][1].h=41;for(int i=1;i<=cnt[1];++i) ++lis[a[1][i].h];
int ct1=1,ct2=1;
while(ct1<cnt[0]){
while(ct2<=cnt[1]&&a[1][ct2].v+a[0][ct1].v<c) --lis[a[1][ct2].h],++ct2;
ans+=cnt[1]-ct2+1;
for(int i=0;i<=n;++i) if(H[i]<a[0][ct1].h) ans-=lis[H[i]];
++ct1;
}printf("%lld
",ans);
return 0;
}