• Luogu4774 NOI2018 屠龙勇士 ExCRT


    传送门


    原来NOI也会出裸题啊……

    用multiset求出对付每一个BOSS使用的武器威力$ATK_i$,可以得到$m$个式子$ATK_ix equiv a_i mod p_i$

    看起来可以直接魔改式子了……

    等一下!如果$a_i > p_i$,$ATK_ix<a_i$没把BOSS打死怎么办QAQ

    看数据范围,没有特性1((a_i leq p_i))的点似乎$p_i=1$?那不只要保证攻击次数能够把所有BOSS血量打到$leq 0$就行了,,,于是这个顾虑就消除了(虽然要写数据分治)

    考虑上面得到的式子,很像ExCRT,但ExCRT的式子都长$x equiv b_i mod p_i$,这里的式子不长这样。于是考虑改式子。

    如果$gcd(ATK_i , p_i) otmid a_i$,显然原式无解。

    当$gcd(ATK_i , p_i) mid a_i$时,求出$ATK_ix + p_iy = gcd(ATK_i,p_i)(的一组解)(x_1,y_1)$,那么$ATK_ix + p_iy = a_i$的一组解就是$(frac{gcd(ATK_i,p_i)} , frac{gcd(ATK_i,p_i)})$。

    那么$ATK_ix equiv a_i mod p_i$的通解就是$x equiv frac{gcd(ATK_i,p_i)} mod frac{gcd(ATK_i,p_i)}$。

    这个式子长得跟ExCRT的式子相同了,直接套板子即可。

    值得注意的是,因为模数可能超过int,导致可能出现乘法爆long long。解决方案是log龟速乘/long double型快速乘/__int128

    还有一个细节是:因为$a_i leq p_i$所以最后答案可能是$0$,此时应该输出的是最后的模数。

    #include<bits/stdc++.h>
    //this code is written by Itst
    using namespace std;
    
    #define int long long
    int read(){
    	int a = 0;
    	char c = getchar();
    	while(!isdigit(c)) c = getchar();
    	while(isdigit(c)){
    		a = a * 10 + c - 48;
    		c = getchar();
    	}
    	return a;
    }
    
    const int _ = 1e5 + 7;
    map < int , int > swr;
    int hp[_] , p[_] , atk[_] , Atk[_] , N , M;
    
    void exgcd(int a , int b , int &d , int &x , int &y){
    	b == 0 ? (d = a , x = 1 , y = 0) : (exgcd(b , a % b , d , y , x) , y -= a / b * x);
    }
    
    int mul(int x , int y , int MOD){
    	int sum = 0;
    	x %= MOD; y %= MOD;
    	while(y){
    		if(y & 1) sum = sum + x >= MOD ? sum + x - MOD : sum + x;
    		x = x + x >= MOD ? x + x - MOD : x + x;
    		y >>= 1;
    	}
    	return sum;
    }
    
    signed main(){
    #ifndef ONLINE_JUDGE
    	freopen("in","r",stdin);
    	//freopen("out","w",stdout);
    #endif
    	for(int T = read() ; T ; --T){
    		swr.clear();
    		N = read(); M = read();
    		for(int i = 1 ; i <= N ; ++i)
    			hp[i] = read();
    		bool sp = 1;
    		for(int i = 1 ; i <= N ; ++i)
    			sp &= (p[i] = read()) == 1;
    		for(int i = 1 ; i <= N ; ++i)
    			atk[i] = read();
    		for(int i = 1 ; i <= M ; ++i)
    			++swr[read()];
    		for(int i = 1 ; i <= N ; ++i){
    			auto t = swr.upper_bound(hp[i]);
    			if(t != swr.begin()) --t;
    			Atk[i] = t->first;
    			if(!--t->second) swr.erase(t);
    			++swr[atk[i]];
    		}
    		int Mod = 1 , ans = 0;
    		if(sp)
    			for(int i = 1 ; i <= N ; ++i)
    				ans = max(ans , hp[i] / Atk[i] + (bool)(hp[i] % Atk[i]));
    		else
    			for(int i = 1 ; i <= N ; ++i){
    				int a , x , y;
    				exgcd(Atk[i] , p[i] , a , x , y);
    				if(hp[i] % a){
    					ans = -1;
    					break;
    				}
    				int mod = p[i] / a;
    				if(x < 0) x += mod;
    				int tp = mul(x , hp[i] / a , mod);
    				int xs = (mod + tp - ans % mod) % mod;
    				exgcd(Mod , mod , a , x , y);
    				if(xs % a){
    					ans = -1;
    					break;
    				}
    				int newMod = Mod / a * mod;
    				if(x < 0) x += mod / a;
    				ans = (mul(mul(x , xs / a , mod / a) , Mod , newMod) + ans) % newMod;
    				Mod = newMod;
    			}
    		cout << (ans ? ans : Mod) << endl;
    	}
    	return 0;
    }
    
  • 相关阅读:
    删除 node_modules文件夹cmd指令
    vue 限制输入字符长度
    vertical-align和text-align属性实现垂直水平居中
    二分查找法
    MySQL实现分页查询
    数据库连接
    AOP编程的常用实现方式
    链表中环的入口
    AQS同步组件及ReentrantLock和synchronized的区别
    快速排序的递归和非递归
  • 原文地址:https://www.cnblogs.com/Itst/p/10350614.html
Copyright © 2020-2023  润新知