【HNOI 2017】大佬

【HNOI 2017】大佬

Problem

Description

人们总是难免会碰到大佬。他们趾高气昂地谈论凡人不能理解的算法和数据结构,走到任何一个地方,大佬的气场就能让周围的人吓得瑟瑟发抖,不敢言语。你作为一个 OIer,面对这样的事情非常不开心,于是发表了对大佬不敬的言论。 大佬便对你开始了报复,你也不示弱,扬言要打倒大佬。现在给你讲解一下什么是大佬,大佬除了是神犇以外,还有着强大的自信心,自信程度可以被量化为一个正整数 \(C\),想要打倒一个大佬的唯一方法是摧毁 Ta 的自信心,也就是让大佬的自信值等于 \(0\)(恰好等于 \(0\),不能小于 \(0\))。由于你被大佬盯上了,所以你需要准备好 \(n\) 天来和大佬较量,因为这 \(n\) 天大佬只会嘲讽你动摇你的自信,到了第 \(n+1\) 天,如果大佬发现你还不服,就会直接虐到你服,这样你就丧失斗争的能力了。

你的自信程度同样也可以被量化,我们用 \(\mathrm{mc}\) 来表示你的自信值上限。在第 \(i \ (i\ge 1)\) 天,大佬会对你发动一次嘲讽,使你的自信值减小 \(a_i\),如果这个时刻你的自信值小于 \(0\) 了,那么你就丧失斗争能力,也就失败了(特别注意你的自信值为 \(0\) 的时候还可以继续和大佬斗争)。 在这一天,大佬对你发动嘲讽之后,如果你的自信值仍大于等于 \(0\),你能且仅能选择如下的行为之一

  1. 还一句嘴,大佬会有点惊讶,导致大佬的自信值 \(C\) 减小 \(1\)

  2. 做一天的水题,使得自己的当前自信值增加 \(w_i\),并将新自信值和自信值上限 \(\mathrm{mc}\) 比较,若新自信值大于 \(\mathrm{mc}\),则新自信值更新为 \(\mathrm{mc}\)。例如,\(\mathrm{mc} = 50\),当前自信值为 \(40\),若 \(w_i = 5\),则新自信值为 \(45\),若 \(w_i = 11\),则新自信值为 \(50\)

  3. 让自己的等级值 \(L\)\(1\)

  4. 让自己的讽刺能力 \(F\) 乘以自己当前等级 \(L\),使讽刺能力 \(F\) 更新为 \(F\cdot L\)

  5. 怼大佬,让大佬的自信值 \(C\) 减小 \(F\)。并在怼完大佬之后,你自己的等级 \(L\) 自动降为 \(0\),讽刺能力 \(F\) 降为 \(1\)。由于怼大佬比较掉人品,所以这个操作只能做不超过两次

特别注意的是,在任何时候,你不能让大佬的自信值为负,因为自信值为负,对大佬来说意味着屈辱,而大佬但凡遇到屈辱就会进化为更厉害的大佬直接虐飞你。在第 \(1\) 天,在你被攻击之前,你的自信是满的(初始自信值等于自信值上限 \(\mathrm{mc}\)),你的讽刺能力 \(F\)\(1\),等级是 \(0\)

现在由于你得罪了大佬,你需要准备和大佬正面杠,你知道世界上一共有 \(m\) 个大佬,他们的嘲讽时间都是 \(n\) 天,而且第 \(i\) 天的嘲讽值都是 \(a_i\)。不管和哪个大佬较量,你在第 \(i\) 天做水题的自信回涨都是 \(w_i\)。这 \(m\) 个大佬中只会有一个来和你较量(\(n\) 天里都是这个大佬和你较量),但是作为你,你需要知道对于任意一个大佬,你是否能摧毁他的自信,也就是让他的自信值恰好等于 \(0\)。和某一个大佬较量时,其他大佬不会插手。

Input Format

第一行三个正整数 \(n,m,\mathrm{mc}\)。分别表示有 \(n\) 天和 \(m\) 个大佬,你的自信上限为 \(\mathrm{mc}\)

接下来一行是用空格隔开的 \(n\) 个数,其中第 \(i(1\le i\le n)\) 个表示 \(a_i\)

接下来一行是用空格隔开的 \(n\) 个数,其中第 \(i(1\le i\le n)\) 个表示 \(w_i\)

接下来 \(m\) 行,每行一个正整数,其中第 \(k(1\le k\le m)\) 行的正整数 \(C_k\) 表示第 \(k\) 个大佬的初始自信值。

Output Format

\(m\) 行,如果能战胜第 \(k\) 个大佬(让他的自信值恰好等于 0),那么第 \(k\) 行输出 \(1\),否则输出 \(0\)

Sample

Input

30 20 30
15 5 24 14 13 4 14 21 3 16 7 4 7 8 13 19 16 5 6 13 21 12 7 9 4 15 20 4 13 12
22 21 15 16 17 1 21 19 11 8 3 28 7 10 19 3 27 17 28 3 26 4 22 28 15 5 26 9 5 26
30
10
18
29
18
29
3
12
28
11
28
6
1
6
27
27
18
11
26
1

Output

0
1
1
0
1
0
1
1
0
0
0
1
1
1
1
1
1
0
0
1

Range

对于 \(20\%\) 的数据,\(1\le n\le 10\)

另有 \(20\%\) 数据,\(1\le C_i,n,\mathrm{mc}\le 30\)

对于 \(100\%\) 的数据,\(1\le n, \mathrm{mc}\le 100, 1\le m\le 20; 1\le a_i, w_i\le\mathrm{mc}, 1\le C_i\le 10^8\)

Algorithm

\(DP\),广搜

Mentality

我感觉我做了一道搜索题 \(......\) 蓝瘦。而且题面也太过真实了。

我们第一个瞬间就该发现,每个大佬只有血量不同,那么我们为了打败大佬,肯定要用尽量多的时间来降低他的自信,则由于其他量都相等,我们完全可以先求出最多能用多少天来与大佬战♂斗对抗。

这个很简单,来一发 \(DP\) 就好,设 \(f[i][j]\) 为到了第 \(i\) 天剩余 \(j\) 点自信时,最多能花多少天嘲讽大佬。然后取 \(n^2\) 数组内的最大值 \(D\) 作为最大天数。

然后我们要统计如何分配怼大佬的那两次。

首先,如果大佬的自信值小于等于 \(D\) ,我们直接不停还嘴就行。

那么我们讨论一下怼大佬的情况。

先计算出我们的嘲讽能力为 \(F\) 时所需花费的最小天数 \(D\) ,求法后面再讲,它太过暴力了 \(emm...\)

我们考虑枚举两次怼大佬时的嘲讽能力 \(F1,F2\) ,显然在相同 \(F\) 值的情况下花在准备上的时间要越少越好,我们设两者的时间为 \(D1,D2\)

那么我们这两次怼大佬必定要满足两个条件:不能把大佬怼死了;剩下的时间通过还嘴可以刚好打败大佬。

转化成不等式如下:

\[ F1+F2\le C ,F1+F2+(D-D1-D2)\ge C \]

那我们只需要枚举一个 \(F1\) ,在这种情况下,\(F1,D1\) 都已经固定了,我们可以找到一个满足第一个不等式的,具有最优性的 \(F2\) ,也即 \(F2-D2\) 在满足第一个不等式的条件下最小,那么这时我们计算 \(F1+F2+(D-D1-D2)\) 是否大于等于 \(C\) ,如果大于则此次询问答案为 \(YES\) ,如果枚举遍所有的 \(F1\) 都无法满足第二个不等式那答案就为 \(NO\)

所以我们可以把所有计算出来的状态按 \(F\) 排序,然后从大到小枚举 \(F1\) ,而 \(F2\) 的寻找范围也是单调递增的,那么我们扫 \(F2\) 的指针沿用上次的即可。

那么重点来了,怎么计算状态呢?答案是 -- 广搜 \(......\)

我们只需要带着三个数值所代表的状态 \(dfs\) 即可,分别是 \(step\) -- 使用 \(step\) 天,\(L\) -- 当前等级,\(F\) -- 当前嘲讽能力。然后直接广搜肯定是不行的,我们来观察一下:由于在相同的 \(F\) 下,所花天数越小越好。而由于我们使用的是广搜,所以当我们搜索到一个新状态 \(step,L,F\) 时,相对于当前的 \(L,F\)\(step\) 就肯定是最优天数了。所以我们只需要使用哈希表判重,如果后面再搜到相同的 \(L,F\) ,那就不加入搜索队列了。

暴力否?

如若不懂详见代码。

为了卡常长得蛮奇怪。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
#define f(i) STA[i].first
#define d(i) STA[i].second
const int mod=9e5+1;
int n,m,limit,maxx,cnt,C,a[101],w[101],c[21];
int f[101][101],D;
inline void read(int &x)
{
    x=0;
    char ch=getchar();
    while(!isdigit(ch))
        ch=getchar();
    while(isdigit(ch))
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
}
pair <int,int> STA[1000001];
struct node{int L,F,t;};
struct Check
{
    int x[mod],y[mod],nx[mod],head[mod],cnt;
    int get_key(int X,int Y){return (998244ll*X+Y)%mod;}
    inline void add(int X,int Y)
    {
        int now=get_key(X,Y);
        cnt++;
        nx[cnt]=head[now],x[cnt]=X,y[cnt]=Y;
        head[now]=cnt;
    }
    inline bool query(int X,int Y)
    {
        int now=get_key(X,Y);
        for(register int i=head[now];i;i=nx[i])
            if(X==x[i]&&Y==y[i])
                return true;
        return false;
    }
}Map,M;
inline void Max(int &a,int b){a=a<b?b:a;}
queue <node> q;
inline void bfs()
{
    q.push((node){0,1,1});
    while(!q.empty())
    {
        node now=q.front();
        q.pop();
        if(now.t==D)continue;
        q.push((node){now.L+1,now.F,now.t+1});
        if(now.L>1&&1ll*now.L*now.F<=maxx&&!Map.query(now.L*now.F,now.L))//手写 Map 判重
        {
            int A=now.L*now.F,B=now.t+1;
            q.push((node){now.L,A,B});
            if(!M.query(A,9181283))STA[++cnt]=make_pair(A,B),M.add(A,9181283);//相同 F 下所花天数越少越好
            Map.add(A,now.L);
        }
    }
}
int main()
{
    read(n),read(m),read(limit);
    for(register int i=1;i<=n;i++)read(a[i]);
    for(register int i=1;i<=n;i++)read(w[i]);
    for(register int i=1;i<=m;i++)read(c[i]),Max(maxx,c[i]);//先处理出搜索的上限 -- F 值至少不能大于自信值最强的大佬的自信吧
    for(register int i=1;i<=n;i++)
        for(register int j=a[i];j<=limit;j++)
            Max(f[i][j-a[i]],f[i-1][j]+1),Max(f[i][min(limit,j-a[i]+w[i])],f[i-1][j]);//先 DP 出最大天数
    for(register int i=1;i<=n;i++)for(register int j=1;j<=limit;j++)Max(D,f[i][j]);//取 max
    bfs();//开始广搜
    sort(STA+1,STA+cnt+1);
    for(register int i=1;i<=m;i++)
    {
        if(c[i]<=D)
        {
            printf("1\n");
            continue;
        }
        int mmax=-1e9,Ans=0;
        for(register int l=1,r=cnt;r;r--)
        {
            while(f(l)+f(r)<=c[i]&&l<cnt)Max(mmax,f(l)-d(l)),l++;//移动
            if(f(r)+D-d(r)+mmax>=c[i])Ans=1;//是否满足第二个不等式
            if(f(r)<=c[i]&&f(r)+D-d(r)>=c[i])Ans=1;
        }
        printf("%d\n",Ans);
    }
}
posted @ 2019-04-09 19:57 洛水·锦依卫 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值