链接:P4774

前言:

交了18遍最后发现是多组数据没清空/ll


题意:

其实就是个扩中。


分析过程:

首先发现根据题目描述的选择剑的方式,每条龙对应的剑都是固定的,有查询前驱,后继(在该数不存在前驱时,最小值即为后继),和插入,删除操作,所以想到平衡树维护每条龙的剑的攻击力,记为b[i]。建议使用非旋treap,非常之好写。


根据题目描述,a[i]为每条龙生命值,p[i]为每条龙回复量。发现能够击杀这条龙的条件可以列成一个方程:

\(xb[i]-yp[i]=a[i]\)

\(x\) 为攻击次数,\(y\) 为回复次数,转化为同余方程的形式为:

\(xb[i]\equiv a[i]\pmod {p[i]}\)

所以题目就被我们转化成了一个一元 \(n\) 次的不定方程组:

\(\begin{cases}xb[1]\equiv a[1]\pmod {p[1]} \\ xb[2]\equiv a[2]\pmod {p[2]}\\ \vdots\\ xb[n]\equiv a[n]\pmod {p[n]}\end{cases}\)

求 \(x\) 的最小非负整数解。

这样的形式虽然不满足CRT和exCRT的形式,但我们可以从exCRT的思想得到启发。我们记录下前 \(m-1\) 个方程的通解 \(x=x_0+t*M\),也就是说我们已知 \(x_0\) 和 \(M\)。

对于第 \(m\) 个方程 \(xb[m]-yp[m]=a[m]\) ,将上述 \(x\) 带入,有

\((x_0+t*M)b[i]-yp[i]=a[i]\)

即 \(x_0b[i]+t*M*b[i]-yp[i]=a[i]\)

由于\(x_0,b[i],M,p[i],a[i]\) 全部都已知,所以化为

\(t*Mb[i]-y*p[i]=a[i]-x_0b[i]\)

设 \(ta=Mb[i],tb=p[i],tc=a[i]-x_0b[i]\)

就有 \(t*ta-y*tb=tc\)

可以用扩展欧几里得求解 \(t=t_0+q*\frac{tb}{\gcd(ta,tb)}\)。

如果 \(t\) 无解那显然此题就无解。

为了这里看起来简洁一些,设 \(r=\frac{tb}{\gcd(ta,tb)}\)。

则 \(t=t_0+q*r\)

发现我们不停带入化简求出了前 \(m-1\) 个方程的解 \(x=x_0+t*M\) 中 \(t\)的范围,所以再将 \(t\) 带入该式,有:

\(x=x_0+(t_0+q*r)*M\)

即 \(x=x_0+t_0*M+q*r*M\)

还看不出来吗?那就再括起来:

\(x=(x_0+t_0*M)+q*(r*M)\)

这个写法是不是和 \(x=x_0+t*M\) 有点像?没错,这就是合并了两个方程之后的解。

发现我们完全通过柿子和推导得出了合并方程的方法,只需要做一次扩展欧几里得。那么对于第一个方程怎么办呢,我们当然可以特殊处理第一个方程,求出它的解。还有另一种巧妙的办法,就是将 \(x\) 一开始的取值范围设为 \(\mathbb{Z}\),只需将 \(M\) 设为 \(1\),即 \(x=x_0+t*1\) ,这里的 \(x_0\) 对结果应该没有影响,我测试了几个不同的初值都能过,这样就不用单独处理第一个方程了。


坑1:

注意我们转化方程 \(xb[i]-yp[i]=a[i]\) 的过程是有缺陷的,根据题意,这里的 攻击次数 \(x\) 和回复次数 \(y\) 显然都应该是非负数而且有一定限制,翻译成人话就是说你砍出来的伤害至少要大于这条龙的血量。再翻译成数学语言就是:

\(\forall i\ (i\in \left [ 1,n \right ]),xb[i]\geq a[i]\)

转化不等式:\(x\geq \frac{a[i]}{b[i]}\)

所以用double类型记录最大的 \(\frac{a[i]}{b[i]}\),把最后的解处理一下即可。


坑2:

因为此题数据范围较大,long long会溢出,所以需要在各种地方取模并使用龟速乘防止溢出。由于我们记录的一直是通解,所以\(x_0\) 的值并不要求最小,只是为了避免溢出所以让 \(x_0\) 每次对 \(M\) 取模。


坑3:

就是我亲身踩了17次的注意平衡树每次会有剩下的剑还在树中,注意清空。


代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e5+5; int read(){
int p=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){p=p*10+c-'0';c=getchar();}
return p*f;
} //treap-------------------
#define v(x) kin[x].v
#define lc(x) kin[x].lc
#define rc(x) kin[x].rc
#define siz(x) kin[x].siz
#define rnd(x) kin[x].rnd
struct k{
int v,lc,rc,siz,rnd;
}kin[2*maxn];
int rt,cnt;
int newnode(int x){
rnd(++cnt)=rand();
siz(cnt)=1;
v(cnt)=x;
lc(cnt)=rc(cnt)=0;
return cnt;
}
void pushup(int x){
siz(x)=siz(lc(x))+siz(rc(x))+1;
}
int merge(int x,int y){
if(!x||!y)return x|y;
if(rnd(x)<rnd(y)){
rc(x)=merge(rc(x),y);
pushup(x);
return x;
}
else{
lc(y)=merge(x,lc(y));
pushup(y);
return y;
}
}
void split(int p,int k,int &x,int &y){
if(!p){x=y=0;return ;}
if(v(p)<=k){
x=p;
split(rc(p),k,rc(p),y);
pushup(p);
}
else{
y=p;
split(lc(p),k,x,lc(y));
pushup(p);
}
}
void insert(int v){
int x,y,z;
x=y=z=0;
split(rt,v,x,z);
y=newnode(v);
rt=merge(merge(x,y),z);
}
void del(int v){
int x,y,z;
x=y=z=0;
split(rt,v,x,z);
split(x,v-1,x,y);
y=merge(lc(y),rc(y));
rt=merge(merge(x,y),z);
}
int getb(int a){
int x,y;
x=y=0;
split(rt,a,x,y);
if(x){
int now=x;
while(rc(now))now=rc(now);
now=v(now);
rt=merge(x,y);
del(now);
return now;
}
else{
int now=y;
while(lc(now))now=lc(now);
now=v(now);
rt=merge(x,y);
del(now);
return now;
}
}
//treap---------------------------- int exgcd(int a,int b,int &x,int &y){
if(a<b)return exgcd(b,a,y,x);
if(a%b==0){
x=0;y=1;
return b;
}
else{
int tx,ty;
int d=exgcd(b,a%b,tx,ty);
x=ty;
y=tx-a/b*ty;
return d;
}
}
int gsc(int ta,int tb,int mod){
if(!ta||!tb)return 0;
int ans=0,f=1;
if(ta<0)ta=-ta,f*=-1;
if(tb<0)tb=-tb,f*=-1;
ta%=mod;
tb%=mod;
while(tb){
if(tb&1)ans=(ans+ta)%mod;
ta=(ta+ta)%mod;
tb>>=1;
}
return ans*f;
}
int T;
int n,m;
int a[maxn],p[maxn],q[maxn],b[maxn];
int x0,M;
signed main(){
T=read();
while(T--){
cnt=0,rt=0;
n=read(),m=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<=n;i++)
p[i]=read();
for(int i=1;i<=n;i++)
q[i]=read();
for(int i=1;i<=m;i++){
int temp=read();
insert(temp);
}
for(int i=1;i<=n;i++){
b[i]=getb(a[i]);
insert(q[i]);
}
x0=0,M=1;
int ta,tb,tc,t,y,d,flag=1;
double mx=0;
for(int i=1;i<=n;i++){
ta=M*b[i],tb=p[i],tc=a[i]-x0*b[i];
d=exgcd(ta,tb,t,y);
if(tc%d!=0){
printf("-1\n");
flag=0;
break;
}
ta/=d,tb/=d,tc/=d;
t=(t%tb+tb)%tb;
x0+=gsc(gsc(tc,t,M*tb),M,M*tb);
M*=tb;
x0=(x0%M+M)%M;
if(double(a[i])/b[i]>mx)
mx=double(a[i])/b[i];
}
while(x0<mx)x0+=M;
if(flag)
printf("%lld\n",x0);
}
return 0;
}

题外话:

做完感觉自己就是屠龙勇士,另外感觉这道题很好的揭示了平衡树这一工具人的作用

洛谷 P4774 [NOI2018] 屠龙勇士的更多相关文章

  1. [洛谷P4774] [NOI2018]屠龙勇士

    洛谷题目链接:[NOI2018]屠龙勇士 因为markdown复制过来有点炸格式,所以看题目请戳上面. 题解: 因为杀死一条龙的条件是在攻击\(x\)次,龙恢复\(y\)次血量\((y\in N^{* ...

  2. (伪)再扩展中国剩余定理(洛谷P4774 [NOI2018]屠龙勇士)(中国剩余定理,扩展欧几里德,multiset)

    前言 我们熟知的中国剩余定理,在使用条件上其实是很苛刻的,要求模线性方程组\(x\equiv c(\mod m)\)的模数两两互质. 于是就有了扩展中国剩余定理,其实现方法大概是通过扩展欧几里德把两个 ...

  3. 洛谷P4774 [NOI2018]屠龙勇士 [扩欧,中国剩余定理]

    传送门 思路 首先可以发现打每条龙的攻击值显然是可以提前算出来的,拿multiset模拟一下即可. 一般情况 可以搞出这么一些式子: \[ atk_i\times x=a_i(\text{mod}\ ...

  4. P4774 [NOI2018]屠龙勇士

    P4774 [NOI2018]屠龙勇士 先平衡树跑出打每条龙的atk t[] 然后每条龙有\(xt \equiv a[i](\text{mod }p[i])\) 就是\(xt+kp[i]=a[i]\) ...

  5. luogu P4774 [NOI2018]屠龙勇士

    传送门 这题真的是送温暖啊qwq,而且最重要的是yyb巨佬在Day2前几天正好学了crt,还写了博客 然而我都没仔细看,结果我就同步赛打铁了QAQ 我们可以先根据题意,使用set维护,求出每次的攻击力 ...

  6. BZOJ5418[Noi2018]屠龙勇士——exgcd+扩展CRT+set

    题目链接: [Noi2018]屠龙勇士 题目大意:有$n$条龙和初始$m$个武器,每个武器有一个攻击力$t_{i}$,每条龙有一个初始血量$a_{i}$和一个回复值$p_{i}$(即只要血量为负数就一 ...

  7. BZOJ_5418_[Noi2018]屠龙勇士_exgcd+excrt

    BZOJ_5418_[Noi2018]屠龙勇士_exgcd+excrt Description www.lydsy.com/JudgeOnline/upload/noi2018day2.pdf 每次用 ...

  8. uoj396 [NOI2018]屠龙勇士

    [NOI2018]屠龙勇士 描述 小 D 最近在网上发现了一款小游戏.游戏的规则如下: 游戏的目标是按照编号 1∼n 顺序杀掉 n 条巨龙,每条巨龙拥有一个初始的生命值 ai .同时每条巨龙拥有恢复能 ...

  9. 洛谷P4774 BZOJ5418 LOJ2721 [NOI2018]屠龙勇士(扩展中国剩余定理)

    题目链接: 洛谷 BZOJ LOJ 题目大意:这么长的题面,就饶了我吧emmm 这题第一眼看上去没法列出同余方程组.为什么?好像不知道用哪把剑杀哪条龙…… 仔细一看,要按顺序杀龙,所以获得的剑出现的顺 ...

随机推荐

  1. MYSQL order by 排序的一个小问题探究

    小问题发现: select * from `sql` where id=1 order by (select 1 union select 2) 正常返回结果 mysql> select * f ...

  2. 496. 下一个更大元素 I

    496. 下一个更大元素 I 给定两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集.找到 nums1 中每个元素在 nums2 中的下一个比其大的值. ...

  3. CodeForce-807C Success Rate(二分数学)

    Success Rate CodeForces - 807C 给你4个数字 x y p q ,要求让你求最小的非负整数b,使得 (x+a)/(y+b)==p/q,同时a为一个整数且0<=a< ...

  4. PHP中的那些魔术常量

    之前我们已经了解了一些常用的魔术方法,除了魔术方法外,PHP还提供一些魔术常量,相信大家在日常的工作中也都使用过,这里给大家做一个总结. 其实PHP还提供了很多常量但都依赖于各类扩展库,而有几个常量是 ...

  5. PHP的引用计数是什么意思?

    什么是引用计数 在PHP的数据结构中,引用计数就是指每一个变量,除了保存了它们的类型和值之外,还额外保存了两个内容,一个是当前这个变量是否被引用,另一个是引用的次数.为什么要多保存这样两个内容呢?当然 ...

  6. 对象赋值在PHP中到底是不是引用?

    之前的文章中,我们说过变量赋值的问题,其中有一个问题是对象在进行变量赋值的时候,直接就是引用赋值.那么到底真实情况是怎样呢? 之前变量赋值的文章 PHP的变量赋值 对象引用测试 在继续深入的学习PHP ...

  7. 痞子衡嵌入式:MCUBootUtility v3.4发布,支持串行NAND

    -- 痞子衡维护的 NXP-MCUBootUtility 工具距离上一个大版本(v3.3.0)发布过去 4 个多月了,这一次痞子衡为大家带来了版本升级 v3.4.0,这个版本主要有几个非常重要的更新需 ...

  8. CF1370F2-The Hidden Pair(Hard Version)【交互题,二分】

    正题 题目链接:https://www.luogu.com.cn/problem/CF1370F2 题目大意 \(T\)组数据,给出\(n\)个点的一棵树,有两个隐藏的关键点.你每次可以询问一个点集, ...

  9. P1251-餐巾计划问题【费用流】

    正题 题目链接:https://www.luogu.com.cn/problem/P1251 题目大意 \(N\)天,第\(i\)天需要\(a_i\)个餐巾. 每个餐巾价格为\(p\),使用完后有两种 ...

  10. AT4502-[AGC029C]Lexicographic constraints【二分,栈】

    正题 题目链接:https://www.luogu.com.cn/problem/AT4502 题目大意 给出\(n\)个长度\(S\),求一个最小\(m\)表示用大小为\(m\)的字符集构造出\(n ...