zoukankan      html  css  js  c++  java
  • 【2020.9.29 NOIP模拟赛 T3】寻梦(fantasy)

    题目链接

    题意

    多组询问,每次询问 (n) 能否能划分成若干 (k) 的因数(可重)的和。

    (T le 10^4,n le 10^{18},k le 10^{15})。保证每个测试点内不同的 (k) 的数量不超过 (50)

    题解

    显然只用考虑 (k) 的质因数。题意可以表述成是否存在 (p_1x_1+p_2x_2 + p_3x_3 + ... + p_mx_m=n) 的一组非负整数解。这时同余最短路的经典模型。找出 (k) 的最小质因数 (p_1),那么要求就转化为初始点为0,每次可以加 (p_2,...,p_m),用最短路算法处理出让当前的点的值模 (p_1) 的意义下同余于 (i) 的最小的当前的值是多少,每个 (n) 直接查 (n mod p_1) 的最小值是否大于 (n),据此判断即可。复杂度:(O(50 imes p_1log p_1 + T))

    不过这个算法可以被一个质数 (k) 卡死。所以需要对质数进行特判(直接判是否是倍数)。但是两个大质数还是能卡死我们,于是还需要对两个大质数进行特判,即我们需要知道是否存在 (p_1x_1+p_2x_2=n) 的非负整数解。这个可以通过 exgcd 搞出 (x_1) 的最小非负整数解,此时 (x_2) 尽可能的大;如果此时都无法满足要求,那么就不可行,否则可行。复杂度:(O(50 imes sqrt[3]{k} log sqrt[3]{k} + Tlog p))

    过于模板,就不放代码了。(其实是没写)

    另解(错解)

    根据某年NOIPD1T1凯哥的疑惑,两个整数 (a,b) 能够通过相加构成 (ab-a-b) 以上的所有数。于是我们可以取出最小的 (p_1,p_2),以此作为一个上限 :(limi = p_1 imes p_2 - p_1 - p_2)。对于 (n > limi) 的情况可以直接判掉。对于 (n le limi) 的情况,我们可以直接完全背包找出所有能组成的数,直接判断 (n) 即可。但是 (limi) 可能很大,我们没办法背包背那么多,于是我们在 (limi > 3 imes 10^7) 的时候只背 (le 3 imes 10^7) 的数。此时 (k) 最多有三个质数,如果有 (0,1,2) 个质数,方法同上;如果有 (3) 个质数,我们可以对于每个 (le limi)(n),枚举最大的两个质数的系数来判断。这样的复杂度为 (O(50 imes 3 imes 10^7 imes 3(k 的质因子个数) + T imes frac{p_1p_2}{p_2} imes frac{p_1p_2}{p_3}) = O(4 imes 10^9 + T imes p_1p_2))。由于数据水,复杂度跑不满。

    bitset<N> bt;
    ll limi;
    ll k_pri[N], kpcnt;
    bool flag;
    inline void work(ll k) {
    	flag = false;
    	limi = 1e18;
    	kpcnt = 0;
    	bt.reset();
    	if (k == 1) {
    		flag = true;
    		return ;
    	}
    	for (int i = 1; i <= pcnt; ++i) {
    		if (k % pri[i] == 0) {
    			k_pri[++kpcnt] = pri[i];
    			while (k % pri[i] == 0)	k /= pri[i];
    			if (k == 1)	break;
    		}
    	}
    	if (k > 1)	k_pri[++kpcnt] = k;
    	if (kpcnt <= 2)	return ;
    	k_pri[kpcnt + 1] = 1;
    	limi = k_pri[1] * k_pri[2] - k_pri[1] - k_pri[2];
    	bt[0] = 1;
    	ll up = min(limi + 1, 1ll * N);
    	for (int i = 1; i <= kpcnt && k_pri[i] <= limi; ++i)
    		for (ll d = k_pri[i]; d < up; ++d)
    			bt[d] = bt[d] | bt[d - k_pri[i]];
    }
    
    inline void ADD(ll &a, ll &b, ll &P) 
    inline ll quickmul(ll x, ll k, ll P) 
    void exgcd(ll a, ll b, ll &x, ll &y)
    
    inline bool sol(ll n) {
    	if (flag)	return false;
    	if (kpcnt == 1)	return n % k_pri[1] == 0;
    	if (kpcnt == 2) {
    		ll x, y, p1 = k_pri[1], p2 = k_pri[2];
    		exgcd(p1, p2, x, y);
    		x = quickmul(x, n, p2);
    		if (1.0 * x * p1 > 2e18)	return false;
    		return n - x * p1 >= 0;
    	}
    	if (n > limi)	return true;
    	if (n < N)	return bt[n];
    	ll t = k_pri[3], tt = k_pri[2], ttt = k_pri[1];
    	for (ll d = 0; d <= n; d += t) {
    		for (ll dd = 0; dd + d <= n; dd += tt) {
    			if ((n - d - dd) % ttt == 0)	return true;
    		}
    	}
    	return false;
    }
    
    int main() {
    	int _; read(_);
    	if (_ >= 13)	up = 2.19e7;
    	else	up = 1e6;
    	init();
    	int tcn; read(tcn);
    	for (int i = 1; i <= tcn; ++i) read(tc[i].n), read(tc[i].k), tc[i].id = i;
    	sort(tc + 1, tc + 1 + tcn, cmp);
    	for (int i = 1; i <= tcn; ++i) {
    		if (tc[i].k != tc[i - 1].k) work(tc[i].k);
    		ans[tc[i].id] = sol(tc[i].n);
    	}
    	for (int i = 1; i <= tcn; ++i) {
    		puts(ans[i] ? "YES" : "NO");
    	}
    	return 0;
    }
    

    总结反思

    又一次把带着调试语句以及十来个 bug 的代码交了上去...毕竟这种题半个小时是很难写完调完的,并且还是在考试的最后半个小时。还是不够熟练,想的时间太长,大概想了一个小时。啥时候我才能有 zzz 那么熟练啊?

    我在洛谷做的题和 zzz 差不了多少,但是可能我做题时自己思考得少一些,可能以后要学着 zzz 半个小时没做出来再看题解。

    反正膜zzz就对了。

  • 相关阅读:
    阅读 图解HTTP ,读书笔记
    javascript 红宝书笔记之操作日期
    设计模式之抽象工厂模式
    设计模式之工厂方法模式
    数据库知识点③
    设计模式之装饰者模式
    设计模式之观察者模式
    心理控制方法——阅读Notes
    使用PL/SQL编写存储过程访问数据库
    《认知盈余》——阅读感受与体会
  • 原文地址:https://www.cnblogs.com/JiaZP/p/13752829.html
Copyright © 2011-2022 走看看