1. 程式人生 > >幾道雜題

幾道雜題

【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