题目链接:https://codeforces.com/contest/1111/problem/C
题目大意:有一堆复仇者在不同的位置上,刚开始有2^n个位置,每次可以分成相等的两部分,如果其中不包含复仇者,毁灭这一部分需要A,如果包含,那就是B*长度*复仇者人数,问毁灭所有位置最小代价
题目思路:首先很容易想到每个位置一定只要三种情况,第一、没有人了返回A,这种情况很明显,在没有人的情况下越分越亏,所以遇到没人肯定直接返回A,第二,左右两部分都有人,那么假设一共k人,左边有x人,那么分开后有2^(n-1)*B*x+2^(n-1)*B*(k-x)=2^(n-1)*B*k必x小于2^n*B,所以左右两部分有人一定是分开好,还有一种情况是左边有人右边没人,这样就是看分开来好还是不分开来好。其实到这里最关键的其实是得到区间内的人数,这也将决定我们接下来的操作。如果没人了,那就返回A,如果还有人,那就先算不分需要的代价,如果还可以继续分,那就继续递归。如何得到人数?直接对位置进行排序,得到大于r的位置和大于等于l的位置相减即可。当时没往人数这个方向想跑偏了于是一直没想出来,以后还是得多注意一些看似不重要的量。
以下是代码:
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(ll i=a;i>=b;i--)
#define ll long long
using namespace std;
const ll MAXN = 1e5+5;
ll a[MAXN],n,k,A,B;
ll dfs(ll l,ll r){
ll num=upper_bound(a+1,a+k+1,r)-lower_bound(a+1,a+k+1,l);
if(num==0)return A;
ll mid=(l+r)>>1;
ll ans=B*(r-l+1)*num;
if(l!=r)ans=min(ans,dfs(l,mid)+dfs(mid+1,r));
return ans;
}
int main()
{
while(~scanf("%I64d%I64d%I64d%I64d",&n,&k,&A,&B)){
rep(i,1,k)scanf("%I64d",&a[i]);
sort(a+1,a+k+1);
ll ans=dfs(1,1<<n);
printf("%I64d\n",ans);
}
return 0;
}