zoukankan      html  css  js  c++  java
  • NOI2018 屠龙勇士 做题心得

    今天A掉了这个题,感觉这是一个帮助我增强调试技巧的好题!!1

    题解

    首先分析,我们发现:对于每条龙,我们用什么伤害的剑,其实是确定的,与 (x) 无关,可以用简单的模拟求出这个东西。

    然后相当于是这样一个方程:

    [egin{cases} a_1 x equiv b_1 pmod{m_1}\ a_2 x equiv b_2 pmod{m_2}\ ...\ a_n x equiv b_n pmod{m_n}\ end{cases} ]

    对于每一条方程 (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;
    }
    

    数据生成器:

    这个代码暂时不在我手上,我明天去学校里搞吧。

  • 相关阅读:
    boost::asio在VS2008下的编译错误
    Java集合框架——接口
    ACM POJ 3981 字符串替换(简单题)
    ACM HDU 1042 N!(高精度计算阶乘)
    OneTwoThree (Uva)
    ACM POJ 3979 分数加减法(水题)
    ACM HDU 4004 The Frog's Games(2011ACM大连赛区第四题)
    Hexadecimal View (2011ACM亚洲大连赛区现场赛D题)
    ACM HDU 4002 Find the maximum(2011年大连赛区网络赛第二题)
    ACM HDU 4001 To Miss Our Children Time (2011ACM大连赛区网络赛)
  • 原文地址:https://www.cnblogs.com/LightningUZ/p/15404565.html
Copyright © 2011-2022 走看看