几道杂题

版权声明:蒟蒻写的文章,能看就行了,同时欢迎大佬们指点错误 https://blog.csdn.net/Algor_pro_king_John/article/details/82942932
【NOIP2018模拟10.5】同余方程
Problem
  • 给定 x [ l 1 , r 1 ] , y [ l 2 , r 2 ] x\in [l_1, r_1],y\in [l_2,r_2] ,求 x   y 0 ( m o d   m ) x\bigoplus \ y \equiv 0 (mod\ m) 的正整数解对个数.

  • l 2 , r 2 1 0 18 , m 1 0 9 l_2, r_2\le 10^{18}, m\le 10^9

Solution
  • 这是道比较巧的题,想法实际上很简单,但实现有点小麻烦.

  • 首先,我们把询问容斥一下.

  • 问题转化为在 x [ 0 , A ] , y [ 0 , B ] x\in [0, A], y\in [0,B] 条件下,满足 x   y 0 ( m o d   m ) x\bigoplus \ y \equiv 0 (mod\ m) 的正整数解对个数.

  • 然后我们把上界给拆分一下,比如说上界是 A = 1101101 A=1101101(二进制) ,那么我们可以拆分为 10 _ _ _ _ _ 1100 _ _ _ 11010 _ _ 10\_\_\_\_\_,1100\_\_\_,11010\_\_,……

  • 其中 _ \_ 表示可以填任意数.

  • 那么对于 A , B A,B 都可以类似的拆分,然后我们可以把 A , B A,B 看出分别固定的一段,以及分别任意的一段。

  • 对于公共的固定段,显然异或之后是固定的,对于仅是 A A 或仅是 B B 的固定段,实际上也可以得到任意数,但方案唯一。

  • 而对于都不是 A , B A,B 的固定段,则显然也可以得到任意数,但方案数并不唯一.

  • 然后就可以按照上面的做法模拟即可.

#include <iostream>
#include <cstring>
#include <cstdio>

#define ll long long
#define F(i, a, b) for (ll i = a; i <= b; i ++)
#define G(i, a, b) for (ll i = a; i >= b; i --)
#define mem(a, b) memset(a, b, sizeof a)
#define lowbit(x) ((x) & (-x))
#define get getchar()

const ll N = 1000, Mo = 998244353;

using namespace std;

ll l1, r1, l2, r2, m, t, d1[N], d2[N], shl[N];
struct node { ll s, l; } x1[N], x2[N];

ll calc(ll x, ll y) {
	if (x < y) swap(x, y);

	for (mem(d1, 0), t = x; t ; d1[++ d1[0]] = t & 1, t >>= 1);
	for (mem(d2, 0), t = y; t ; d2[++ d2[0]] = t & 1, t >>= 1);

	ll now = 0, L1 = 0, L2 = 0, Answer = 0;

	G(i, d1[0], 1) if (d1[i]) x1[++ L1] = {now, d1[0] - i + 1}, now += shl[i - 1]; x1[++ L1] = {now, d1[0] + 1}, now = 0;
	G(i, d2[0], 1) if (d2[i]) x2[++ L2] = {now, d2[0] - i + 1}, now += shl[i - 1]; x2[++ L2] = {now, d2[0] + 1};

	F(i, 1, L1) {
		ll top1 = d1[0], top2 = d2[0], go1 = d1[0], go2 = d2[0], sum1 = 0, sum2 = 0;
		F(j, 1, L2) {
			while (top1 > 1 && d1[0] - top1 < x1[i].l - 1) top1 --;
			while (top2 > 1 && d2[0] - top2 < x2[j].l - 1) top2 --;
			while (go1 && go1 >= top2 && d1[0] - go1 < x1[i].l - 1) sum1 += shl[go1 - 1] * d1[go1 -- ];
			while (go2 && go2 >= top1 && d2[0] - go2 < x2[j].l - 1) sum2 += shl[go2 - 1] * d2[go2 -- ];

			if (min(top1, top2) == 0) continue;
			ll sma = sum1 ^ sum2, big = sma + shl[max(top1, top2) - 1] - 1, ans = shl[min(top1, top2) - 1] % Mo;
			Answer = (Answer + ans * (((big / m) - ((sma - 1) / m) + (sma == 0)) % Mo)) % Mo;
		}
	}

	return Answer - (x / m) - (y / m);
}

int main() {
	freopen("mod.in", "r", stdin);
	freopen("mod.out", "w", stdout);

	scanf("%lld %lld %lld %lld %lld", &l1, &r1, &l2, &r2, &m), shl[0] = 1;
	F(i, 1, 63) shl[i] = shl[i - 1] * 2LL;
	printf("%lld\n", (calc(r1, r2) - calc(r1, l2 - 1) - calc(r2, l1 - 1) + calc(l1 - 1, l2 - 1) + Mo) % Mo);
}


【NOIP2018模拟10.5】旅游
Problem
  • 给定 { n , m } \{n,m\} 的图,第 i i 条边的长度是 2 i 2^i ,求从原点 1 1 经过所有边回到原点且总长度最小的路径长度.

  • n , m 5 1 0 5 n,m\le 5*10^5

Solution
  • 这题的思路也很巧.

  • 因为原图必定有偶数个度为奇的点,所以显然,是要把这偶数个奇的点两两配对.

  • 那么构出原图的最小生成树,显然配对的边必须要出现在最小生成树中.

  • 并且,可以证明,每条边只会在至多 1 1 个配对中用到.

  • 如何判断一条边是否会被用到?很简单,只需判断删掉这条边后原图分成的两颗树是否都是奇数个奇数点,如果是,则证明,不管以后怎么匹配,两棵树都会剩下一个点要进行匹配,那么这条边就一定会被选上.


【NOIP2018模拟10.5】机器人
Problem
  • 长度为 n n 01 01 串, T T 次变换,每次 N e w [ i ] = l a s [ i 1 ] l a s [ i + 1 ] New[i] = las[i - 1]\bigoplus las[i + 1] .

  • n 1 0 5 , T 1 0 18 n\le 10^5, T\le 10^{18}

Solution
  • 好吧,考试的时候真的不知道是脑残了还是脑残了。

  • 2 2 意义下可以矩阵乘法。

  • 一个牛逼的结论是,在二的次幂下,每行至多两个转移点。

  • 然后记录一下就好了。


5892. 【NOIP2018模拟10.4】矿石
Problem
  • 给定 n n [ L i , R i ] [L_i,R_i] 的区间,以及 m m 个点,要求有多少种选择区间的方案,使得至少有一个点可以覆盖这种方案选择的所有区间.

  • n 1 0 5 n\le 10^5

Solution
  • 可以考虑从左到右枚举点.

  • 一个点会产生贡献,显然是有新的线段覆盖了这个点,但没有覆盖先前已经枚举过的点.

  • 于是堆维护即可.


5893. 【NOIP2018模拟10.4】括号序列
Problem
  • 给定一个字符串,一个合法的括号序满足对应括号的对应字符相同.

  • 求区间合法括号序个数。

  • S 1 0 6 |S|\le 10^6

Solution
  • 一个很常见,但又巧妙的思路。

  • 首先显然能配对就配对一定是最优的。

  • 对于每个括号序,hash一下,map当前括号序的hash,累加答案即可(Trie也可以)


3382. 【NOIP2013模拟】七夕祭
Problem
  • n m n*m 的网格有一些点,要求上下左右移动点,使得每行每列点个数相同。
Solution
  • 首先不考虑环,那么这就是个均分纸牌问题.

  • 答案显然是 i = 1 n S i i m \sum_{i=1}^n|S_i-i*m| 其中 S i = j = 1 i a j S_i=\sum_{j=1}^ia_j .

  • a i = a i m a_i=a_i-m S i = j = 1 i a j S_i=\sum_{j=1}^ia_j ,则答案是 i = 1 n S i \sum_{i=1}^n|S_i|

  • 考虑环,发现,必定有两个相邻的格子没有点移动.

  • 枚举这个位置 k k ,则可以把答案表示为 A n s = i = 1 n S i S k Ans = \sum_{i=1}^n|S_i-S_k|

  • 显然这就是个中位数.


4307. 【NOIP2015模拟11.3晚】喝喝喝
Problem
  • 给定 n , K n,K 及长度为 n n 的数组,求合法的区间个数,一个区间合法当且仅当这个区间不存在整数对 x , y x,y ,满足 x &lt; y , a x K ( m o d   a y ) x\lt y,a_x\equiv K(mod\ a_y)

  • n 1 0 5 , K 1 0 5 , a i 1 0 5 n\le 10^5, K\le 10^5,a_i\le 10^5

Solution
  • 首先可以在根号的时间复杂度内找到一个数能往右扩展到的最右位置。

  • 这样,我们可以得到许多扩展区间.

  • 显然如果一个大区间包含一个小区间,那么大区间是没有用的.

  • 所以就得到许多个一环套一环的区间,然后就可以计算答案了.

  • 当然,这是比较zz的做法,事实上,我们可以设 L [ i ] L[i] 表示从 i i 这个位置往前,仅仅依靠 i i 这一个数作为 a y a_y ,满足条件的 a x a_x 中最靠右的 x x .

  • 如何得到 L [ i ] L[i] ?事实上可以直接从左到右扫一遍,枚举一个数的所有因数,然后记录一下,求 L [ i ] L[i] 的时候就可以用了.


4919. 【NOIP2017提高组模拟12.10】神炎皇
Solution
  • 考虑证明一个结论,当 x , y x,y 互质时, x + y x+y x y xy 互质.

  • 一种反证法:

    • 假设 d x + y , d x y d|x+y,d|xy ,且 d &gt; 1 d\gt 1 .
    • 因为 d x y d|xy ,所以 d d 必定是 x x y y 的因数,不妨令 i d = x id=x .
    • d x d|x ,结合 d x + y d|x+y ,可以得到 d y d|y ,所以与条件矛盾.
    • 原命题成立.
  • 另一种反证法(By me & Doggy_Zheng):

    • 基于 x y xy 不是 x + y x+y 的倍数,并不是互质.
    • 假设 x y = k ( x + y ) xy=k(x+y) ,其中 k k 取可能值中最大值,即必定满足 k x k|x k y k|y .
    • 不妨假设 k y k|y .
    • 因为 x y = k x + k y xy=kx+ky ,则有 x ( y k ) = k y x(y-k)=ky ,即 k k y k y-k 的倍数。
      • 可以再次反证,即假设 d k , d y k d|k,d|y-k ,则可以推出 d y d|y ,故与假设矛盾.
    • 故与假设矛盾.
    • 原命题成立。
  • 回到原题,题目要我们求 x + y x y x+y|xy ,则可以枚举 g c d gcd d d ,使得 x = x d , y = y d x=x&#x27;d,y=y&#x27;d .

  • 即要求 d x + d y d 2 x y dx&#x27;+dy&#x27;|d^2x&#x27;y&#x27; ,转化可以得到 x + y d x y x&#x27;+y&#x27;|dx&#x27;y&#x27; ,通过上面的结论知道 x y x&#x27;y&#x27; 必定不是 x + y x&#x27;+y&#x27; 的倍数.

  • 所以 d d 必定是 x + y x&#x27;+y&#x27; 的倍数,因为 d x + d y n dx&#x27;+dy&#x27;\le n ,所以 ( x + y ) &lt; n (x&#x27;+y&#x27;)&lt;\sqrt{n}

  • 不妨枚举 s = x + y s=x&#x27;+y&#x27; ,则合法 d d n s 2 \frac{n}{s^2} 个,合法的 s s 则对应有 φ ( S ) \varphi(S) 个,乘起来就是答案了.

猜你喜欢

转载自blog.csdn.net/Algor_pro_king_John/article/details/82942932