今天A掉了这个题,感觉这是一个帮助我增强调试技巧的好题!!1
题解
首先分析,我们发现:对于每条龙,我们用什么伤害的剑,其实是确定的,与 (x) 无关,可以用简单的模拟求出这个东西。
然后相当于是这样一个方程:
对于每一条方程 (axequiv bpmod{m}),我们把它变形。由于我们会excrt,我们知道,肯定是变成 (xequiv bpmod{m}) 好做。
考虑 (g=gcd(a,m))。如果 (b) 不是 (g) 的倍数,啪的一下,无解。这是显然的。否则,我们可以把 (a,b,m) 同事约去一个 (g),然后 (a,m) 就互质力!!!
互质,那好做,变成 (xequiv b imes a^{-1} pmod{m})
然后就做一下 excrt 就行了
小结
首先那个观察非常重要,不过相对容易些
然后就是对式子的分析。就和学校里whk老师讲的做题方法一样,首先你手上要有一套方法,然后你要想,这个题目的问题,怎么转化成你手上的这些东西。
就好比我们会excrt,会做的其实是 (xequiv bpmod{m})。而这个题目中和这个形式不同,因此我们要做转化,把它变成会做的这个形式。
关于调试
这题的数据卡的很死,因此尤其要小心爆long long的问题。
有一个常识是,exgcd是不会爆的。一开始是hyh和我讲的这个事情,我后来去u群确认了一下。
证:EI说对,那就是对!
其它地方会不会爆呢?这我们不好直接靠脑子想。先把代码写出来,过小样例再说。如果小样例你感觉太水,可以手造一些。手造数据的能力很重要,后面的对拍都需要靠手造样例。
关于造数据:
您可以先生成一个 (x),再随机一些 (a) 和 (m),计算 (axmod m),得到 (b)。有一件事情是,我们需要保证所有 (m) 的 (lcm) 不超过 (1e12)
这并不困难,我们手造一个 (1e12) 左右,因数比较多的数。然后每次的 (m) 就随机取它一个因数就行
这里又有一个小技巧:随机取因数,不需要先求出因数,我们在造这个数的时候,就先把它分解,然后每个质数随机一个指数就行了。
对于小数据,可以把这个数取的小一些,比如 (1e8)
然后我们可以用 Linux 下的 -fsanitize=undefined
功能,找到哪些地方爆了long long。
然后多调几次就行了。
对于最后三个点,可能比较毒瘤。这里暴露的问题也许很难在调试中发现,如果实在不行就下数据。我个人感觉,靠手造数据调试,是可以做到85分的。
代码
本题
#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define N 200005
#define int long long
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Tra(i,u) for(int i=G.h[u],v=G.to(i);~i;i=G.nx(i),v=G.to(i)) if (i>=0)
#define MEM(a,x) memset(a,x,sizeof(a))
#define FK(a) MEM(a,0)
#define sz(x) ((int)x.size())
#define all(x) x.begin(),x.end()
#define p_b push_back
#define pii pair<int,int>
#define fir first
#define sec second
int I() {char c=getchar(); int x=0; int f=1; while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar(); while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return ((f==1)?x:-x);}
template <typename T> void Rd(T& arg){arg=I();}
template <typename T,typename...Types> void Rd(T& arg,Types&...args){arg=I(); Rd(args...);}
void RA(int *p,int n) {F(i,1,n) *p=I(),++p;}
int n,m;
int a[N],p[N],g[N],h[N];
void Input(int id=0)
{
Rd(n,m);
F(i,1,n) a[i]=I();
F(i,1,n) p[i]=I();
F(i,1,n) g[i]=I();
F(i,1,m) h[i]=I();
}
int smul(int a,int b,int m) // 龟速乘, 您可以用 __int128 代替(雾)
{
a=(a%m+m)%m; b=(b%m+m)%m;
int r=0;
while(b)
{
if (b&1) r=(r+a)%m;
a=(a<<1)%m; b>>=1;
}
return r;
}
int gcd(int a,int b){if (a<0) a=-a; if (b<0) b=-b; while(b)swap(a,b),b%=a; return a;}
int lcm(int a,int b){return a/gcd(a,b)*b;} // lcm,gcd: 不会爆
void exgcd(int a,int b,int&x,int&y) // ax+by=gcd(a,b), 这个也不会爆
{
if (b==0) {x=1; y=0; return;}
exgcd(b,a%b,y,x); y-=x*(a/b);
}
int inv(int a,int b){int x,y; exgcd(a,b,x,y); return (x%b+b)%b;}
bool sol(int a,int b,int&x,int&y,int c,int mod) // ax+by=c, 返回是否有解
{
b=-b; // b一定是负的
int g=gcd(a,b);
if (c%g) {x=-1; y=-1; return false;}
a/=g; b/=g; c/=g;
exgcd(a,b,x,y); // ax+by=1
x=smul(x,c,mod); y=smul(y,mod-c,mod); // 小心!
return true;
}
namespace exCRT
{
bool no_sol=0;
int r[N],m[N],n; // x = r[i] (mod m[i])
int L=1; // 所有模数的lcm
void clear()
{
n=0; FK(r); FK(m); no_sol=0; L=1;
}
void add(int a,int b,int mm) // 加入一条方程 ax=b (mod m)
// a<=1e6, b<=1e12, mm<=1e12
{
if (mm==1) return; // 我们跳过模数为1的方程
int g=gcd(a,mm);
if (b%g) {no_sol=1; return;}
a/=g; b/=g; mm/=g;
b=smul(b,inv(a,mm),mm); // 小心!
++n;
r[n]=b; m[n]=mm;
L=lcm(L,m[n]);
}
int get_sol() // 解
{
if (n==0) {return 0;} // 有一个sb情况就是, 所有模数都为1, 不过不判好像没事
if (no_sol) {return -1;}
int R=r[1],M=m[1]; // M<=1e12, R<M
F(i,2,n)
{
int x,y;
int m2=lcm(M,m[i]);
if (sol(M,-m[i],x,y,r[i]-R,m2))
{
R=(R+smul(x,M,m2))%m2; // 小心!
M=m2;
}
else return -1;
}
return R;
}
}
multiset<int>sw; multiset<int>::iterator it,it2;
void Sakuya()
{
exCRT::clear(); sw.clear();
F(i,1,m) sw.insert(h[i]);
int mn=0;
// 注意到我们的每次攻击都至少要把龙打到0血以下,因此我们的x值是有下界的
F(i,1,n)
{
it2=sw.upper_bound(a[i]);
if (it2!=sw.begin()) --it2;
int atk=*it2; // 找剑
exCRT::add(atk,a[i],p[i]);
mn=max(mn,(a[i]+atk-1)/atk); // 要打到0以下
sw.erase(it2); sw.insert(g[i]);
}
int R=exCRT::get_sol(),M=exCRT::L;
if (R==-1)
{
puts("-1");
}
else
{
if (R<mn) R=R+M*(mn-R+M-1)/M; // 这个其实就是不断的加, 直到加过下界
printf("%lld
",R);
}
}
void IsMyWife()
{
int t=I(); int Cas=0;
while(t-->0)
{
++Cas;
Input(Cas);
Sakuya();
}
}
}
#undef int //long long
int main()
{
Flandre_Scarlet::IsMyWife();
return 0;
}
数据生成器:
这个代码暂时不在我手上,我明天去学校里搞吧。