1. 程式人生 > 實用技巧 >2020 8,9月做題記錄

2020 8,9月做題記錄

CF1076F Summer Practice Report

題目描述:給定 \(n\) 個字串,第 \(i\) 個包含 \(x_i\)a\(y_i\)b,將它們按順序首尾相接,求是否有可能不存在連續 \(k\) 個字元相同。

資料範圍:\(n\le 3\times 10^5,x_i,y_i,k\le 10^6\)

直接貪心即可,每個字串都是一種字元插入另一種字元中。

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int mod = 998244353;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
const int N = 300003;
int n, k; LL x[N], y[N];                            
int main(){
	read(n); read(k);
	for(Rint i = 1;i <= n;++ i) read(x[i]);
	for(Rint i = 1;i <= n;++ i) read(y[i]);
	LL tmp = 0;
	for(Rint i = 1;i <= n;++ i){
		tmp = max(0ll, x[i] + tmp - k * y[i]);
		if(tmp > k) return puts("NO"), 0;
	} tmp = 0;
	for(Rint i = 1;i <= n;++ i){
		tmp = max(0ll, y[i] + tmp - k * x[i]);
		if(tmp > k) return puts("NO"), 0;
	} puts("YES");
}

CF919F A Game With Numbers

題目描述:Alice 和 Bob 在玩♂遊戲,他們分別有 8 張卡片,每張卡片上有 \([0,4]\) 的數字。兩人輪流操作,Alice 先手,每次取自己和對方各一張牌 \(a,b\),要求 \(ab\ne 0\),然後將 \(a\) 變為 \((a+b)\bmod 5\)。先將牌變為全 \(0\) 的人獲勝,問最終的勝負情況。\(T\) 組資料。

資料範圍:\(T\le 10^5\)

直接暴力列舉所有狀態並連邊即可...

-CF930E Coins Exhibition

題目描述:問有多少個長為 \(k\)\(01\) 字串,滿足 \(n+m\)

個限制。有 \(n\) 個形如 \([l_i,r_i]\) 不為全 \(0\),有 \(m\) 個形如 \([l_i,r_i]\) 不為全 \(1\)。答案對 \(10^9+7\) 取模。

資料範圍:\(k\le 10^9,n,m\le 10^5\)

先對所有的 \(l_i-1\)\(r_i\) 離散化。

大概...直接容斥+dp?

-CF933E A Preponderant Reunion

題目描述:給定長為 \(n\) 的自然數序列,每次你可以選擇兩個相鄰的正整數 \(p_i,p_{i+1}\),然後將它們都減去 \(\min(p_i,p_{i+1})\)。問最小的操作次數使不能再操作,並構造方案。

資料範圍:\(n\le 3\times 10^5,p_i\le 10^9\)

CF891E Lust

題目描述:給定 \(n\) 個數 \(a_1,a_2,\dots,a_n\),進行 \(k\) 次操作,每次隨機選擇一個數 \(x\in[1,n]\),然後將 \(a_x\) 減去 \(1\),將答案加上其他所有數的乘積,求答案期望\(\bmod(10^9+7)\)

資料範圍:\(n\le 5000,a_i,k\le 10^9\)

容易發現答案就是初始狀態乘積減去結束狀態乘積。

現在要計算的就是

\[\begin{aligned} &k![x^k]\prod_{i=1}^n\sum_j\frac{a_i-j}{j!}x^j \\ =&k![x^k]\prod_{i=1}^n(a_i\sum_j\frac{1}{j!}x^j-\sum_j\frac{x^j}{(j-1)!}) \\ =&k![x^k]\prod_{i=1}^n(a_i-x)e^x \\ =&k![x^k]e^{nx}\prod_{i=1}^n(a_i-x) \end{aligned} \]

\(\prod_{i=1}^n(a_i-x)=\sum_{i=0}^nf_ix^i\),則這個柿子的值為 \(\sum_{i=0}^nf_in^{k-i}k^{\underline{i}}\)。時間複雜度 \(O(n\log^2n)\)

CF819E Mister B and Flight to the Moon

題目描述:給定 \(n\) 個點的無向完全圖,求用長為 \(3,4\) 的環將每條邊恰好覆蓋 \(2\) 次的方案。

資料範圍:\(n\le 300\)

首先考慮 \(n\) 是奇數的情況,可以先不管 \(1\) 號點,然後將剩下的點兩兩配對。先從 \(1\) 連出一些三角形把配對的邊去掉,然後再考慮每兩對之間的 \(4\) 條邊,用兩個長度為 \(4\) 的環覆蓋即可。

然後考慮 \(n\) 是偶數的情況,不管 \(1,2,3,4\) 號點,將剩下的點兩兩配對。每兩對之間的邊用同樣的方法即可,然後考慮 \(1,2,3,4\) 號點的連邊,用 \(3\) 個長度為 \(4\) 的環覆蓋即可。然後再考慮 \(1,2,3,4\) 與其他組的連邊。先用 \((1,i,i+1)\)\((2,i,i+1)\) 解決 \((i,i+1)\) 的邊,然後再用 \(2\) 個長度為 \(4\) 的環覆蓋。具體實現可以看程式碼,時間複雜度 \(O(n^2)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int mod = 998244353;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n;
int main(){
	read(n);
	if(n & 1){
		printf("%d\n", (n * n - 1) >> 2);
		for(Rint i = 2;i < n;i += 2){
			printf("3 1 %d %d\n", i, i + 1);
			printf("3 1 %d %d\n", i, i + 1);
			for(Rint j = 2;j < i;j += 2){
				printf("4 %d %d %d %d\n", i, j, i + 1, j + 1);
				printf("4 %d %d %d %d\n", i, j, i + 1, j + 1);
			}
		}
	} else {
		printf("%d\n", (n * n >> 2) - 1);
		puts("4 1 2 3 4"); puts("4 1 3 4 2"); puts("4 1 4 2 3");
		for(Rint i = 5;i < n;i += 2){
			printf("3 1 %d %d\n", i, i + 1);
			printf("3 2 %d %d\n", i, i + 1);
			printf("4 1 %d 2 %d\n", i, i + 1);
			for(Rint j = 3;j < i;j += 2){
				printf("4 %d %d %d %d\n", i, j, i + 1, j + 1);
				printf("4 %d %d %d %d\n", i, j, i + 1, j + 1);
			}
		}
	}
}

CF933D A Creative Cutout

AGC047E Product Simulation

本題是提交答案題

題目描述:使用不超過 \(2\cdot10^5\) 個值域為 \([0,10^{19}]\) 的整型,和不超過 \(2\cdot10^5\) 個操作,實現兩個 \([0,V]\) 的整數 \(a[0],a[1]\) 的乘法。每次操作形如 \(a[k]=a[i]+a[j]\)\(a[k]=a[i]<a[j]\)

資料範圍:部分分 \(V=10\),滿分 \(V=10^9\)

不知為何,首先考慮將這兩個數進行二進位制拆分。

首先考慮如何算出 \(1\),可以用 \((0<a[0]+a[1])\),因為如果 \(a[0]=a[1]=0\) 的話無法做出非 \(0\) 數,所以是沒有關係的。然後就可以加法堆到 \(2^k(k\in[0,30))\)

然後考慮 \(a[k]=\max(a[i]-a[j],0)\),主要想法就是將 \(a[j]\) 不斷抬高至 \(a[i]\) 的位置,看看抬高了多少,可以用倍增實現。二進位制拆分也是倍增,從大到小列舉每一位然後減去它即可。

拆分之後,由於每一位都是 \(0\)\(1\),所以可以用 \(1<(a+b)\) 計算 \(ab\),暴力卷積即可,時間複雜度 \(O(\log^2V)\)

CF1305G Kuroni and Antihype

題目描述:有 \(n\) 個人要加入 CCF,每個人有一個權值 \(a_i\),兩個人 \(i,j\) 是朋友當且僅當 \(a_i\ \text{AND}\ a_j=0\)。每次第 \(i\) 個人可以主動加入 CCF,或者已經加入 CCF 的人邀請他的一個朋友加入 CCF 並得到 \(a_i\) 元錢。求總共至多可以 qj CCF 多少錢。

資料範圍:\(n,a_i\le 2\times 10^5\)

我們可以先設一個 dzd,這個人是所有人的朋友且一開始就加入了 CCF。設一條邊的邊權是 \(w_{i,j}=a_i+a_j\),答案就是邊權之和-點權之和,問題就是求解最大生成樹。

直接做 Kruskal,時間複雜度 \(O(3^{18}\alpha(n))\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 300000, m = 1 << 18, mod = 998244353;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, a[N], fa[N]; LL ans;
int getfa(int x){while(x != fa[x]) x = fa[x] = fa[fa[x]]; return x;}
void comb(int x, int y, int val){
	if(a[x] && a[y]){
		x = getfa(x); y = getfa(y);
		if(x != y){ans += (a[x] + a[y] - 1ll) * val; fa[x] = y; a[y] = 1;}
	}
}
int main(){
	read(n); a[m] = 1;
	for(Rint i = 0;i <= m;++ i) fa[i] = i;
	for(Rint i = 1, x;i <= n;++ i){read(x); ++ a[x]; ans -= x;}
	for(Rint i = m - 1;~i;-- i){
		for(Rint j = i;j;j = (j - 1) & i) comb(j, i ^ j, i);
		comb(i, m, i); comb(0, i, i);
	}
	printf("%lld\n", ans);
}

CF1290E Cartesian Tree

題目描述:給定長為 \(n\) 的排列 \(a\),對於每個 \(k\in[1,n]\),將排列中 \(\le k\) 的項構成的子序列建大根笛卡爾樹,求所有節點的子樹大小之和。

資料範圍:\(n\le 1.5\times10^5\)

答案可以用 \(\sum(r_i-l_i-1)\) 表示,其中 \(l_i\) 表示 \(i\) 左邊第一個 \(>a_i\) 的,\(r_i\) 表示 \(i\) 右邊第一個 \(>a_i\) 的。

CF1336D Yui and Mahjong Set

這是一道互動題

題目描述:互動器有一個可重集 \(S\),保證元素屬於 \([1,n]\),且每種元素出現次數不超過 \(n\)。定義一個 \(\mathbf{triplet}\)\(S\) 的大小為 \(3\) 的子集,且這三個元素的值相等。定義一個 \(\mathbf{straight}\)\(S\) 的大小為 \(3\) 的子集,且這三個元素的值連續。你可以進行至多 \(n\) 次查詢,每次查詢你給出一個數 \(x\in[1,n]\),互動器告訴你目前 \(\mathbf{triplet}\)\(\mathbf{straight}\) 的數量,然後將 \(x\) 插入集合 \(S\)。你需要求出初始集合中,對於每個 \(i\in[1,n]\)\(i\) 的出現次數。

資料範圍:\(4\le n\le 100\)

UOJ549 【UNR #4】序列妙妙值

題目描述:給定長為 \(n\) 的自然數序列 \(\{a_i\}\) 和正整數 \(k\),對於所有 \(i\in[k,n]\cap\Z\),求將 \(a_1,a_2,\dots,a_i\) 分為 \(k\) 段,每段異或和之和的最小值。

資料範圍:\(k\le n\le 6\times10^4,k\le 8,a_i<2^{16}\)

跟 ntf 的 sd 題撞 idea 複雜度還被吊打...

\(O(nkv)\) 的暴力 dp 很容易做。但是每一個二進位制位是互相獨立的,使用一個輔助的資料結構,將 dp 賦值和求 \(\min\) 抽象為修改和查詢,修改時列舉高 \(8\) 位更新,查詢時列舉低 \(8\) 位求 \(\min\),就平衡了複雜度,時間複雜度 \(O(nk\sqrt v)\)

UOJ551 【UNR #4】校園閒逛

題目描述:給定一個 \(n\times n\) 的矩陣 \(A\),每個元素是一個次數不超過 \(\max_v\) 的多項式。\(q\) 次詢問 \(x,y,v\),求 \([z^v]A^{-1}_{x,y}\bmod 998244353\)

資料範圍:\(n\le 8,v\le\max_v\le 65000,q\le 10^4\)

UOJ550 【UNR #4】網路恢復

這是一道互動題

題目描述:互動器有 \(n\) 個點 \(m\) 條邊的無向簡單圖,你只知道 \(n,m\)。每次詢問,你可以給每個點定權值,給每條邊(按編號)定黑白顏色,互動器對於每個節點 \(i\) 計算與它通過黑邊相鄰的所有點的點權異或和。你需要詢問至多 \(50\) 次來求出邊集,但你並不需要確定每條邊的編號。

資料範圍:\(n\le 5\times 10^4,m\le 3\times 10^5\)

CF1394

B Boboniu Walks on Graph

題目描述:給定 \(n\) 個點 \(m\) 條邊的有向圖,每個點的出度至多為 \(k\),一個長為 \(k\) 的序列 \((c_1,c_2,\dots,c_k)\) 為合法的當且僅當:

  1. \(1\le c_i\le i\)
  2. 從任意節點 \(u\) 開始行走,每次若當前節點的出度為 \(i\),則選擇連出的邊中編號第 \(c_i\) 小的走,可以在有限時間內回到 \(u\)

求有多少個合法的序列。

資料範圍:\(n,m\le 2\cdot10^5,k\le 9\)

如果滿足題目要求,那麼最終一定會成一個大環。所以序列合法當且僅當 \(\{nxt_{i,c_{deg_i}}|i\in[1,n]\cap\Z\}=[1,n]\cap\Z\),設 \(S_{i,j}=\{nxt_{u,j}|deg_u=i\}\),則變為 \(\bigcup_{i=1}^nS_{i,c_i}=[1,n]\cap\Z\),並且這些集合必定不相交,所以可以用 hash 判斷,時間複雜度 \(O(n+m+k!)\)

C Boboniu and String

題目描述:對於一個 \(01\) 字串 \(s\),你可以進行如下操作:

  1. 刪除任意一個字元;

  2. 刪除任意一個 \(01\)\(10\)

  3. 在末尾加一個字元;

  4. 在末尾加一個 \(01\)\(10\)

兩個字串 \(s,t\) 相似當且僅當 \(|s|=|t|\)\(0\) 的出現次數相等。\(\text{dist}(s,t)\) 是使兩個字串的編輯距離。

給定 \(n\) 個字串 \(s_i\),你可以選擇任意的 \(t\),使得 \(\max_{i=1}^n\text{dist}(s_i,t)\) 最小。求出最小值並構造任意一個 \(t\)

資料範圍:\(n\le 3\cdot10^5,\sum|s_i|\le 5\cdot10^5\)

D Boboniu and Jianghu

題目描述:給定 \(n\) 個點的樹,每個點有權值 \(h_i,t_i\),你要將邊集分割成一些路徑,滿足每條路徑上的 \(h\) 單調遞增,一條路徑的權值為上面所有點的 \(t\) 之和,求所有路徑權值和的最小值。

資料範圍:\(n\le2\cdot10^5,1\le h_i,t_i\le 10^6\)

E Boboniu and Banknote Collection

題目描述:給定一個長為 \(n\) 的序列 \(a_i\),問你最多可以把它沿兩個數之間的分界線對摺多少次,使得相同位置的數相同,並構造一種方案。

形式化定義:設 \(b\) 是一個長為 \(n\) 的序列,稱 \(b\)\(a\) 的摺疊序列當且僅當

  1. \(b_i=\pm 1\).
  2. \(p_i=[b_i=1]+\sum_{j=1}^{i-1}b_j\),則 \(p_i=p_j\Rightarrow a_i=a_j\)

\(b\) 的權值 \(f(b)=\sum_{i=1}^{n-1}[b_i\ne b_{i+1}]\)。求 \(F(a)=\max\{f(b)|b\text{ is a folding sequence of }a\}\)

資料範圍:\(1\le a_i\le n\le 10^5\)

UOJ553 【UNR #4】己酸集合

題目描述:給定平面上 \(n\) 個點,\(q\) 次詢問與 \((0,z)\) 距離不超過 \(r\) 的有多少個點。

資料範圍:\(n\le 1.2\times 10^4,q\le 10^6\)

\(x^2+(y-z)^2\le r^2\)\(x^2+y^2\le r^2-z^2+2zy\),則將 \((x,y)\) 對映到 \((y,x^2+y^2)\),則每次詢問為一條直線 \(y=r^2-z^2+2zx\) 以下有多少個點。

這個題在 ZR 集訓講過,可以用分塊的方法,將所有點平均分成 \(S\) 塊,每塊維護所有 \(k\)\(y-kx\) 排序之後的順序,這種順序只有平方個,處理詢問的時候二分即可,時間複雜度 \(O((\frac{n^2}{S}+qS)\log n)\),取 \(S=\frac{n}{\sqrt q}\) 得到 \(O(n\sqrt q\log n)\)

UOJ552 【UNR #4】同構判定鴨

題目描述:給定兩張 \(n\) 個點 \(m\) 條邊的無向圖 \(G_1,G_2\),每條邊有一個小寫字母。定義字串 \(S\) 和一條路徑匹配當且僅當按順序寫下路徑上邊的字元,得到的字串與 \(S\) 相同。定義字串 \(S\) 在一張圖 \(G\) 的出現次數為 \(G\) 中與 \(S\) 匹配的路徑數。定義字串 \(S\) 合法當且僅當 \(S\)\(G_1,G_2\) 中的出現次數不相等。求最短的合法字串 \(S\),如果長度相同求字典序最小的,若不存在則判斷無解。

資料範圍:\(n\le 500,m\le 3000\)

UOJ554 【UNR #4】挑戰哈密頓

這是一道提交答案題

題目描述:給定 \(n\) 個點 \(m\) 條邊的無向圖,求出一條 Hamilton 路徑,保證有解。

資料範圍:\(n,m\le 5\times 10^5\)

-CF1266H Red-Blue Graph

題目描述:給定 \(n\) 個點的有向圖,前 \(n-1\) 個點的出度為 \(2\),連出一條紅邊和一條藍邊,且有顏色,一開始每個點的顏色是藍的。第 \(n\) 個點沒有出邊,且保證可以從前 \(n-1\) 個點中的任意一個到達。你一開始在 \(1\) 號點,每 \(1s\) 你會反轉你當前所在點的顏色,然後走與當前點相同顏色的出邊。\(q\) 次詢問,每次詢問給出這個圖的狀態 \((v,s)\)\(v\) 表示你當前在的點的編號,\(s\) 表示前 \(n-1\) 個點的顏色,若一開始是 \(0\) 時刻,求最早出現這種狀態的時刻。注意反轉顏色後走到另外一個點之前的狀態是不考慮的。

資料範圍:\(n\le 58,q\le 5000\)

考慮單組詢問 \(\{s_i\}_{i=1}^{n-1}\)\(v\),設 \(x_i,y_i\) 分別是走 \(i\) 連出紅、藍邊的次數,那麼答案就是 \(\sum_{i=1}^{n-1}(x_i+y_i)\)。易得 \(x_i=y_i+[s_i=\text{R}]\),設 \(B_i,R_i\) 分別是藍、紅邊連向 \(i\) 的點集,則考慮進 \(i\) 點和出 \(i\) 點的次數:

\[\sum_{r\in R_i}x_r+\sum_{b\in B_i}y_b+[i=1]=x_i+y_i+[i=v] \]

CF1392H ZS Shuffles Cards

題目描述:給定 \(n+m\) 張撲克牌,其中有 \(n\) 個編號 \([1,n]\cap\Z\),有 \(m\) 個 Joker。你有一個初始為空的集合 \(S\),一開始你把這些牌隨機洗好,並進行以下操作:摸一張牌,

  • 如果這張牌有編號 \(x\),那麼他會拿走這張牌並將 \(x\) 加入集合 \(S\)

  • 如果這張牌是 Joker,他會把所有牌(包括已經拿走的)放在一起,隨機洗好。若此時 \(S=[1,n]\cap\Z\) 則停止遊戲。

求期望時間\(\bmod 998244353\)

資料範圍:\(n,m\le 2\times10^6\)

思考一下,發現抽到鬼牌之間的摸牌情況是獨立的,所以答案就是 期望輪數*每輪的期望時間。

每輪的期望時間就是:

\[E[X]=\frac{(n+m)!}{n!}\sum_{i=0}^n\frac{i!}{(i+m)!} \]

Codechef Challenge AUG20

SUBSFREQ

題目描述:給定一個長為 \(n\) 的正整數序列 \(a_i\),求對於所有 \(x\in[1,n]\cap\Z\),求 \(x\)\(\{a\}\) 的所有非空子序列的眾數(出現次數相同的取較小的)中出現多少次。\(T\) 組資料。

資料範圍:\(T\le 100,\sum n\le 5\cdot10^5,1\le a_i\le n\)

PSTONES

題目描述:給定 \(n\) 個點的樹,邊有 \(k\) 種顏色。對於所有 \(2^k\) 種顏色集合 \(S\)\(m\in[1,n]\cap\Z\),你需要判斷是否存在一個大小為 \(m\) 的連通子集,使得連線這個聯通子集和聯通子集之外的點的所有邊的顏色集合為 \(S\)\(T\) 組資料。

資料範圍:\(\sum n\le 500,k\le 12\)

NOI2020

D1T1 美食家

題目描述:給定 \(n\) 個點 \(m\) 條邊的有向圖,經過第 \(i\) 條邊 \((u_i,v_i)\) 需要 \(w_i\) 的時間,第 \(i\) 個點有 \(c_i\) 的權值。還有 \(k\) 個三元組 \((t_i,x_i,y_i)\),表示在 \(t_i\) 時刻第 \(x_i\) 個點的權值會加上 \(y_i\)。一個包含第 \(1\) 個點的環的權值為,你在 \(0\) 時刻從第 \(1\) 個點出發,沿著這個環走,沿途遇到的所有點的權值之和。求環權值的最大值。

資料範圍:\(n\le 50,n\le m\le 501,k\le 200,t_i\le 10^9,w_i\le 5,c_i\le 52501\)

D1T2 命運

題目描述:給定 \(n\) 個點的以 \(1\) 為根的樹,和 \(m\) 個點對 \((u_i,v_i)\),滿足 \(u_i\ne v_i\)\(u_i\)\(v_i\) 的祖先。求有多少種給邊黑白染色的方法,使得所有 \(u_i\)\(v_i\) 的路徑上必有黑邊。

資料範圍:\(n,m\le 5\times10^5\)

\(dp_{u,i}\) 表示考慮 \(u\) 的子樹,\(u\) 往上最近的黑邊的深度為 \(i\) 的方案數。

\[dp_{u,i}=\prod_{(u,v)\in E}(dp_{v,dep_v}+dp_{v,i}) \]

D1T3 時代的眼淚

題目描述:給定長為 \(n\) 的排列 \(p\)\(m\) 次詢問,每次詢問給出 \((r_1,r_2,c_1,c_2)\),表示在所有 \(r_1\le i\le r_2,c_1\le p_i\le c_2\) 的位置中,有多少個順序對。

資料範圍:\(n\le10^5,m\le2\times10^5\)

D2T1 製作菜品

題目描述:給定正整數 \(n,m,k\)\(n\) 個正整數 \(d_i\),滿足 \(\sum d_i=mk\)。每次操作,你可以使 \(d\) 中 1 或 2 個數的值變小,且變化量之和為 \(k\)。求給出 \(m\) 次操作使 \(d_i=0\) 的一種方案或判斷無解。\(T\) 組資料。

資料範圍:\(T\le 10,n\le 500,n-2\le m\le 5000,k\le 5000\)

D2T2 超現實樹

題目描述:此處定義的”樹“指非空有根,區分左右兒子的二叉樹,這樣的所有樹構成的集合稱為 \(U\)。定義一棵樹 \(T\) 的生長集合 \(\text{grow}(T)\) 為每次可以將 \(T\) 的一個葉子變為一棵樹,得到的所有樹構成的集合。定義一個樹集 \(\mathcal F=\{T_1,T_2,\dots,T_n\}\) 的生長集合 \(\text{grow}(\mathcal F)=\bigcup_{i=1}^n\text{grow}(T_i)\)。每次給定一個樹集 \(\mathcal F\),判斷 \(\complement_U\text{grow}(\mathcal F)\) 是否為有限集。\(T\) 組資料。

資料範圍:\(T\le 100,\sum n\le 2\times10^6\)

D2T3 翻修道路

題目描述:給定 \(n\) 個點 \(m\) 條邊的邊帶權的弦圖和兩個端點 \(s,t\),你要找到一條 \(s\)\(t\) 之間的路徑,使得去掉這個路徑上面的邊時,圖仍然聯通,求這條路徑上邊的權值和的最小值,或判斷無解。

資料範圍:\(n\le 5\times 10^5,m\le 10^6\)

P5405 [CTS2019]氪金手遊

題目描述:給定 \(n\) 個點 \(n-1\) 條邊的有向圖,它對應的邊集相同的無向圖是樹。第 \(i\) 個點有一個權值 \(w_i\),它有 \(j\) 的概率是 \(p_{i,j}\),其中 \(j\in\{1,2,3\}\)。每次以 \(w_i\) 的權重隨機選一個沒有選過的點打上標記,問標記順序是合法拓撲序的方案數。

資料範圍:\(n\le 10^3\)

當這棵樹是以 \(1\) 為根的外向樹時,設 \(sz_i\) 表示以 \(i\) 為根的子樹內的點權之和,則答案為 \(\prod\frac{w_i}{sz_i}\),若有內向邊則容斥,時間複雜度 \(O((nv)^2)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 1003, M = 3003, mod = 998244353;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int ksm(int a, int b = mod - 2){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, head[N], to[N<<1], nxt[N<<1], f[N][M], g[M], inv[M], siz[N], ans;
void add(int a, int b){
	static int cnt = 0;
	to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt;
}
void init(int m){
	inv[1] = 1;
	for(Rint i = 2;i <= m;++ i) inv[i] = mod - (LL) mod / i * inv[mod % i] % mod;
}
void dfs(int x, int fa = 0){
	siz[x] = 3;
	for(Rint i = head[x];i;i = nxt[i]) if(to[i] != fa){
		dfs(to[i], x);
		for(Rint j = 1;j <= siz[x];++ j)
			for(Rint k = 1;k <= siz[to[i]];++ k){
				int val = (LL) f[x][j] * f[to[i]][k] % mod;
				if(i & 1) qmo(g[j+k] += val - mod);
				else {qmo(g[j] += val - mod); qmo(g[j+k] -= val);}
			}
		siz[x] += siz[to[i]];
		for(Rint j = 1;j <= siz[x];++ j) f[x][j] = g[j], g[j] = 0;
	}
	for(Rint j = 1;j <= siz[x];++ j) f[x][j] = (LL) f[x][j] * inv[j] % mod;
}
int main(){
	read(n); init(3*n);
	for(Rint i = 1, a[4];i <= n;++ i){
		read(a[0]); read(a[1]); read(a[2]); a[3] = ksm(a[0] + a[1] + a[2]);
		for(Rint j = 1;j <= 3;++ j) f[i][j] = (LL) j * a[j-1] * a[3] % mod;
	}
	for(Rint i = 1, u, v;i < n;++ i){
		read(u); read(v); add(u, v); add(v, u);
	}
	dfs(1);
	for(Rint i = 1;i <= siz[1];++ i) qmo(ans += f[1][i] - mod);
	printf("%d\n", ans);
}

-CF1060F Shrinking Tree

題目描述:給定 \(n\) 個點的樹,每次等概率選取一條邊,合併這條邊連線的兩個點,新點的編號等概率選取這兩個節點之一,直到只剩下一個節點。對於 \(i\in[1,n]\cap\Z\),求出最後得到的點的編號為 \(i\) 的概率。

資料範圍:\(n\le 50\)

-CF794E Choosing Carrot

題目描述:給定長為 \(n\) 的序列 \(a_i\),Alice 和 Bob 在玩遊戲,一次操作刪掉序列的開頭或結尾,直到剩下唯一一個數,Alice 想讓這個數儘量大,Bob 想讓這個數儘量小,對於 \(k\in[1,n]\cap\Z\),若 Alice 先進行 \(k\) 次操作,Bob 再操作,之後輪流操作,求最後剩下的數。

資料範圍:\(n\le 3\times 10^5,a_i\le 10^9\)


csl

UOJ429 【集訓隊作業2018】串串劃分

題目描述:給定長為 \(n\) 的字串 \(S\),你要將它劃分若干個子串使得 \(s_i\ne s_{i+1}\),且 \(s_i\) 的最短迴圈節是自己。求劃分方案數\(\bmod 998244353\)

資料範圍:\(n\le 2\times 10^5\)

CF356E Xenia and String Problem

題目描述:給定長為 \(n\) 的字串 \(S\),定義一個字串 \(T\) 合法當且僅當 \(T\) 的長度為奇數,且中間的字元只出現一次,且左右兩邊都是合法串或空串。求在修改 \(S\) 的一個字元的情況下,\(S\) 的所有合法子串的長度的平方和的最大值。

資料範圍:\(n\le 10^5\)

CF gym 100221C Forbidden Subwords

題目描述:給定一個字串集合 \(\mathcal F\),求有多少個兩邊無限長的字串 \(\alpha\),使得 \(\mathcal F\) 中的每個字串 \(S\) 都不是 \(\alpha\) 的子串。兩個兩邊無限長的字串 \(\alpha=\beta\) 當且僅當 \(\exist k\in\Z,\forall i\in\Z,\alpha_{i}=\beta_{i+k}\)。需判斷無解。

資料範圍:\(|\mathcal F|\le 10^3,|S|\le 10,|\Sigma|\le 6\)

根據 AC 自動機,可以建立出一個轉移圖,這個圖上的任意一條路徑對應的一個字串均合法。

分析得出,如果這個圖上有一個強聯通分量不是簡單環,那麼無解。然後給強聯通分量縮點,如果縮點之後有一條路徑包含三個簡單環,那麼無解。

於是合法的字串就只能是一個環到另一個環的路徑構成的,直接計數即可。

UOJ499 新年的邀請函

題目描述:給定正整數 \(n,m\)。已知質數 \(p\in[1,m]\),給定 \(10^5\) 個限制 \(a_i,t_i\),表示 \(a_i^{\frac{p-1}{2}}\equiv t_i(\text{mod} \ p)\)。求滿足要求的質數 \(p\),保證有唯一解。

資料範圍:\(1\le a_i\le n\le 10^9,m\le 10^{12}\)\(a_i\)\(p\) 在值域範圍內等概率隨機。

大約有 \(400\) 個數僅包含前 \(25\) 個質數,由於勒讓德符號的積性,可以高斯消元求出前 \(25\) 個質數\(\bmod p\) 是否為二次剩餘。根據二次互反律,可以得出 \(p\) 模前 \(25\) 個質數是否為二次剩餘。

HDU5451 Best Solver

題目描述:給定自然數 \(x\),質數 \(p\),計算 \(\lfloor(5+2\sqrt 6)^{1+2^x}\rfloor\) 對質數 \(p\) 取模,\(T\) 組資料。

資料範圍:\(x<2^{32},p\le 46337,T\le 10^3\)

直接暴力擴域 \((\Z/p\Z)[\sqrt 6]\),則 \((5+2\sqrt 6)^n\) 有周期 \(p^2-1\),直接將 \(1+2^x\)\(p^2-1\) 取模再計算即可。時間複雜度 \(O(\log x+\log p)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
int t, x, p, mod;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & p;}
int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
struct Node {
	int a, b;
	Node(int _a = 0, int _b = 0): a(_a), b(_b){}
	Node operator * (const Node &o) const {return Node(((LL) a * o.a + 6ll * b * o.b) % p, ((LL) a * o.b + (LL) b * o.a) % p);}
};
Node ksm(Node a, int b){
	Node res(1);
	for(;b;b >>= 1, a = a * a) if(b & 1) res = res * a;
	return res; 
}
int solve(){
	read(x); read(p); mod = p * p - 1;
	Node a(5, 2); a = ksm(a, ksm(2, x) + 1); return (a.a * 2 + p - 1) % p;
}
int main(){read(t); for(Rint i = 1;i <= t;++ i) printf("Case #%d: %d\n", i, solve());}

HDU6340 Delightful Formula

題目描述:給定正整數 \(n\) 的質因子分解 \(n=\prod_{i=1}^mp_i^{\alpha_i}\) 和正整數 \(k\),求 \((\sum_{i=1}^n[i\bot n]\sum_{j=1}^ij^k)\bmod 998244353\)\(T\) 組資料。

資料範圍:\(m\le 20,p_i,\alpha_i\le 10^9,k\le 10^5,\sum k\le 10^6\)

\[\begin{aligned} \mathbf{OGF}\{B\}&=\frac{x}{e^x-1}+x \\ \sum_{i=1}^ni^k&=k!\sum_{j=1}^{k+1}B_{k+1-j}\frac{n^j}{j!}\\ F(n,k)&=\sum_{d|n}d^k\mu(d)=\prod_{p|n}(1-p^k)\\ Ans&=\sum_{d|n}\mu(d)\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{id}j^k \\ &=k!\sum_{d|n}\mu(d)\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{k+1}B_{k+1-j}\frac{(id)^j}{j!} \\ &=k!\sum_{d|n}\mu(d)\sum_{j=1}^{k+1}B_{k+1-j}\frac{d^j}{j!}\sum_{i=1}^{\frac{n}{d}}i^j \\ &=k!\sum_{d|n}\mu(d)\sum_{j=1}^{k+1}B_{k+1-j}d^j\sum_{l=1}^{j+1}B_{j+1-l}\frac{n^l}{d^ll!} \\ &=k!\sum_{l=1}^{k+2}\frac{n^l}{l!}\sum_{j=[l=1]}^{k+2-l}B_{k+2-j-l}B_jF(n,j-1) \end{aligned} \]

\(F(n,k)\) 可以對每個 \(k\) 都計算一遍,然後就是卷積的形式。注意最後一個柿子中的變數取值範圍。時間複雜度 \(O(k\log k+m\sum k)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int mod = 998244353, N = 1 << 18;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int t, m, n, k, a[N], b[N], rev[N], lim, w[2][N], fac[N], inv[N], pri[20], alp[20], res;
void init(int m){
	fac[0] = 1;
	for(Rint i = 1;i <= m;++ i) fac[i] = (LL) fac[i-1] * i % mod;
	inv[m] = ksm(fac[m], mod - 2);
	for(Rint i = m;i;-- i) inv[i-1] = (LL) inv[i] * i % mod;
}
void calrev(int len){
	int L = -1; lim = 1;
	while(lim <= len){lim <<= 1; ++ L;}
	for(Rint i = 0;i < lim;++ i) rev[i] = (rev[i>>1]>>1) | ((i&1)<<L);
	for(Rint mid = 1;mid < lim;mid <<= 1){
		w[0][mid] = w[1][mid] = 1; int Wn = ksm(3, (mod - 1) / (mid << 1));
		for(Rint i = 1;i < mid;++ i) w[0][mid + i] = (LL) Wn * w[0][mid + i - 1] % mod;
		for(Rint i = 1;i < mid;++ i) w[1][mid + i] = mod - w[0][(mid<<1) - i];
	}
}
void NTT(int *A, int op){
	for(Rint i = 0;i < lim;++ i) if(i < rev[i]) swap(A[i], A[rev[i]]);
	for(Rint mid = 1;mid < lim;mid <<= 1)
		for(Rint i = 0;i < lim;i += mid << 1)
			for(Rint j = 0;j < mid;++ j){
				int y = (LL) A[mid + i + j] * w[op][mid + j] % mod;
				qmo(A[mid + i + j] = A[i + j] - y); qmo(A[i + j] += y - mod);
			}
	if(op){
		int inv = ksm(lim, mod - 2);
		for(Rint i = 0;i < lim;++ i) A[i] = (LL) A[i] * inv % mod; 
	}
}
int ans[N], tmp[N];
void polyinv(int *A, int deg){
	if(deg == 1){ans[0] = ksm(A[0], mod - 2); return;}
	polyinv(A, deg + 1 >> 1); calrev(deg << 1);
	for(Rint i = 0;i < deg;++ i) tmp[i] = A[i];
	for(Rint i = deg;i < lim;++ i) tmp[i] = 0;
	NTT(tmp, 0); NTT(ans, 0);
	for(Rint i = 0;i < lim;++ i) ans[i] = (2ll - (LL) ans[i] * tmp[i] % mod + mod) * ans[i] % mod;
	NTT(ans, 1);
	for(Rint i = deg;i < lim;++ i) ans[i] = 0;
}
int calc(int d){
	int ans = 1; if(d < 0) d += mod - 1;
	for(Rint i = 0;i < m;++ i)
		ans = ans * (mod + 1ll - ksm(pri[i], d)) % mod;
	return ans;
}
void solve(){
	read(k); read(m); n = 1;
	for(Rint i = 0;i < m;++ i){read(pri[i]); read(alp[i]); pri[i] %= mod; n = (LL) n * ksm(pri[i], alp[i]) % mod;}
	calrev(k + 1 << 1); int tmp = calc(-1); a[0] = tmp; b[0] = 1;
	for(Rint i = 1;i <= k + 1;++ i) a[i] = (LL) ans[i] * calc(i - 1) % mod, b[i] = ans[i];
	for(Rint i = k + 2;i < lim;++ i) a[i] = b[i] = 0;
	NTT(a, 0); NTT(b, 0);
	for(Rint i = 0;i < lim;++ i) a[i] = (LL) a[i] * b[i] % mod;
	NTT(a, 1); res = 0;
	for(Rint i = 1, t = n;i <= k + 2;++ i, t = (LL) t * n % mod)
		qmo(res += (LL) t * inv[i] % mod * a[k + 2 - i] % mod - mod);
	qmo(res -= (LL) n * tmp % mod * ans[k + 1] % mod);
	printf("%d\n", (LL) res * fac[k] % mod);
}
int main(){
	read(t); init(100005);
	for(Rint i = 0;i <= 100000;++ i) a[i] = inv[i+1];
	polyinv(a, 100001); ++ ans[1];
	while(t --) solve();
}

CF232D Fence

題目描述:給定長為 \(n\) 的序列 \(h_i\)\(q\) 次詢問 \(l_1,r_1\),求有多少個 \(l_2,r_2\),使得 \([l_1,r_1]\)\([l_2,r_2]\) 沒有交集,且對應位置為相反數。

資料範圍:\(n,q\le 10^5,|h_i|\le 10^9\).

CodeChef BOUNCE

題目描述:設 \(f(R,C)\) 表示,有一個被四周鏡子圍起來的 \(R\times C\) 的矩形,有一束光線從左下角的角平分線射出,光線按順序碰到上、下、左、右邊界,每次分別記錄 U,D,L,R,直到碰到一角,得到的字串。給定字串 \(S\) 和正整數 \(n\),計算有多少個 \(1\le R,C\le n\),滿足 \(S\)\(f(R,C)\) 的字首。\(T\) 組資料。

資料範圍:\(T\le 5,\sum|S|\le 10^6,4\le n\le 10^{10}\)

CodeChef DIVISORS

題目描述:給定正整數 \(b,x\),計算有多少個正整數 \(n\) 滿足 \(nx\) 有一個因數 \(d\) 滿足 \(n<d\le b\)\(T\) 組資料。

資料範圍:\(T\le 40,b\le 10^{12},x\le 60\)

\(d_\min(n,x)\) 表示 \(nx\)\(>n\) 的最小因數。考慮對於每個 \(k\in[1,x)\),計算 \(k|nx\) 並且 \(\forall k<j<x,j\not|nx\)\(n\) 的個數 \(N(k,x)\)。因為 \(j|nx\Leftrightarrow j_x=\frac{j}{\gcd(j,x)}|n\),設 \(n=k_xm\),所以 \(\forall k<j<x,j\not|k_xmx\Leftrightarrow \frac{j}{\gcd(j,k_xx)}\not|m\)

因為 \(d_\min(n,x)\le b\),所以 \(m\le \frac{bk}{k_xx}\),即 \(m\le \lfloor\frac{b}{x/\gcd(x,k)}\rfloor\)。設 \(c=\lfloor\frac{b}{x/\gcd(x,k)}\rfloor\)\(N(k,x)\)\(m\le c\) 且不被 \(\frac{j}{\gcd(j,k_xx)}(k<j<x)\) 中的任意一個整除,這個可以使用容斥原理計算,算出 \(\frac{j}{\gcd(j,k_xx)}\)

CF720F Array Covering

題目描述:給定長為 \(n\) 的整數序列 \(a_i\) 和正整數 \(k\),找到恰好 \(k\) 個不相同的併為 \([1,n]\) 的區間,使得所有區間包含的元素之和之和最大。

資料範圍:\(n\le 10^5,k\le \frac{n(n+1)}{2},|a_i|\le 5\times 10^4\)

顯然,最大和的 \(k-n\) 個區間必須取。

CF gym 101745 E Increasing Sequence

題目描述:給定長為 \(n\) 的自然數序列 \(x_i\),你需要找到正整數 \(k\),將一部分 \(x_i\) 變為 \(k-x_i\),使得 \(x_i\) 遞增,且 \(x_1\ge 0\)。需判斷無解。

資料範圍:\(n\le 10^5,x_i\le 10^9\)

我們可以考慮相鄰兩個元素 \(x'_i<x'_{i+1}\)\(4\) 種操作方案對應著一個 \(k\) 的值域範圍,從 \(i\)\(i+1\) 的轉移可以用 \(2\times 2\)\(01\) 矩陣表示,將 \(k\) 從小到大掃描線,用線段樹動態維護所有矩陣的乘積,就可以找到合法的 \(k\),構造方案直接貪心或根據矩陣的前/字尾積即可。

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 100003, mod = 998244353;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, x[N], m; bool ans[N];
struct Mat {
	bool a[2][2];
	Mat(){memset(a, 0, sizeof a);}
	Mat operator * (const Mat &o) const {
		Mat res;
		for(Rint i = 0;i < 2;++ i)
			for(Rint k = 0;k < 2;++ k)
				for(Rint j = 0;j < 2;++ j) res.a[i][j] |= a[i][k] & o.a[k][j];
		return res;
	}
} t[N], seg[N<<2], pre[N];
struct Node {
	int val, id, x, y; bool w;
	Node(int v = 0, int id = 0, int x = 0, int y = 0, bool w = false): val(v), id(id), x(x), y(y), w(w){}
	bool operator < (const Node &o) const {return val < o.val;}
} q[N<<2];
void upd(int x, int L, int R, int p){
	if(L == R){seg[x] = t[p]; return;}
	int mid = L + R >> 1;
	if(p <= mid) upd(x<<1, L, mid, p);
	else upd(x<<1|1, mid+1, R, p);
	seg[x] = seg[x<<1] * seg[x<<1|1];
}
int main(){
	read(n);
	for(Rint i = 1;i <= n;++ i) read(x[i]);
	q[++ m] = Node(0, 1, 0, 0, 1); q[++ m] = Node(x[1], 1, 0, 1, 1);
	for(Rint i = 2;i <= n;++ i){
		if(x[i-1] < x[i]) q[++ m] = Node(0, i, 0, 0, 1);
		else if(x[i-1] > x[i]) q[++ m] = Node(x[i-1], i, 1, 1, 1);
		q[++ m] = Node(x[i] + x[i-1] + 1, i, 0, 1, 1);
		if(x[i]){q[++ m] = Node(x[i-1], i, 1, 0, 1); q[++ m] = Node(x[i-1] + x[i], i, 1, 0, 0);}
	}
	sort(q + 1, q + m + 1);
	for(Rint i = 1;i <= m;++ i){
		t[q[i].id].a[q[i].x][q[i].y] = q[i].w;
		upd(1, 1, n, q[i].id);
		if((i == m || q[i].val < q[i+1].val) && (seg[1].a[0][0] || seg[1].a[0][1])){
			printf("%d\n", q[i].val);
			pre[0].a[0][0] = true;
			for(Rint j = 1;j <= n;++ j) pre[j] = pre[j-1] * t[j];
			ans[n] = pre[n].a[0][1];
			for(Rint j = n-1;j;-- j) ans[j] = pre[j].a[0][1] && t[j+1].a[1][ans[j+1]];
			for(Rint j = 1;j <= n;++ j) printf("%d ", ans[j] ? q[i].val-x[j] : x[j]); return 0;
		}
	}
	puts("-1");
}

CF650E Clockwork Bomb

題目描述:給定兩棵 \(n\) 個節點的樹,每次操作將一條邊斷掉,連一條新邊,並保證仍然是樹,求將一棵樹變為另一棵樹的最小操作次數,並構造方案。

資料範圍:\(n\le 5\times 10^5\)

容易得出答案是不相同的邊的個數。因為每次操作必定可以讓不相同的邊的個數 -1.

CF641F

題目描述:給定兩個 2-sat,求使其中一組不成立,另一組成立的變數取值。需判斷無解。

資料範圍:\(n\le 10^3,m_1,m_2\le n^2\)

先建出 2-sat 的圖,然後做一遍傳遞閉包,大力討論:

  1. 兩個都無解,則無解。
  2. 一個有解一個無解,隨便解一個。
  3. 兩個都有解
    1. 存在一個點,一個 2-sat 要求必須為真,另一個可以為真,也可以為假,隨便解一個。
    2. 存在其中一個 2-sat 的一條邊,另一個 2-sat 可以不滿足這條邊的限制,也做完了。
#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 2003, mod = 998244353;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, m0, m1;
struct Graph {
	bitset<N> f[N]; bool ans[N], flag;
	void Set(int u){
		ans[u] = true;
		for(Rint i = 0;i < (n<<1);++ i) if(f[u][i] && !ans[i]) Set(i);
	}
	void build(int m){
		for(Rint i = 0, u, v;i < m;++ i){
			read(u); read(v);
			u = (abs(u)-1 << 1) | (u > 0);
			v = (abs(v)-1 << 1) | (v > 0);
			f[u].set(v^1); f[v].set(u^1);
		}
		for(Rint i = 0;i < (n<<1);++ i) f[i].set(i);
		for(Rint i = 0;i < (n<<1);++ i)
			for(Rint j = 0;j < (n<<1);++ j) if(f[j][i]) f[j] |= f[i];
		for(Rint i = 0;i < (n<<1);i += 2) if(f[i][i+1] && f[i+1][i]){flag = true; break;}
		for(Rint i = 0;i < (n<<1);i += 2)
			if(!(ans[i] || ans[i+1])) if(f[i][i+1]) Set(i+1); else if(f[i+1][i]) Set(i);
	}
	void solve(int x = -1, int y = -1){
		if(~x) Set(x); if(~y) Set(y);
		for(Rint i = 0;i < (n<<1);i += 2)
			if(!(ans[i] || ans[i+1])) Set(i);
		for(Rint i = 0;i < (n<<1);i += 2) printf("%d ", ans[i]);
	}
} g[2];
int main(){
	read(n); read(m0); read(m1);
	g[0].build(m0); g[1].build(m1);
	if(g[0].flag && g[1].flag){puts("SIMILAR"); return 0;}
	if(g[0].flag){g[1].solve(); return 0;}
	if(g[1].flag){g[0].solve(); return 0;}
	for(Rint i = 0;i < (n<<1);++ i)
		if(!g[0].ans[i] && g[1].ans[i]){g[0].solve(i^1); return 0;}
		else if(g[0].ans[i] && !g[1].ans[i]){g[1].solve(i^1); return 0;}
	for(Rint i = 0;i < (n<<1);++ i){
		if(!(g[0].ans[i] || g[0].ans[i^1]))
			for(Rint j = 0;j < i;++ j)
				if(!(g[0].ans[j] || g[0].ans[j^1]) && !g[0].f[i][j] && g[1].f[i][j]){
					g[0].solve(i, j^1); return 0;
				}
		if(!(g[1].ans[i] || g[1].ans[i^1]))
			for(Rint j = 0;j < i;++ j)
				if(!(g[1].ans[j] || g[1].ans[j^1]) && !g[1].f[i][j] && g[0].f[i][j]){
					g[1].solve(i, j^1); return 0;
				}
	} puts("SIMILAR");
}

AGC046D Secret Passage

題目描述:給定字串 \(S\),每次操作你可以刪掉 \(S\) 的前兩個字元,將其中一個插入任意位置,求能得到的字串數量\(\bmod 998244353\)

資料範圍:\(|S|\le 300,|\Sigma|=2\)

LOJ3272「JOISC 2020 Day1」漢堡肉

題目描述:給定 \(n\) 個邊與座標軸平行的矩形 \((L_i,D_i,R_i,U_i)\) 和正整數 \(k\),要求找出 \(k\) 個點 \((x_i,y_i)\),使得每個矩形至少包含一個點。

資料範圍:\(n\le 2\times 10^5,k\le 4,L_i,D_i,R_i,U_i\in[-10^9,10^9]\cap\Z\)。保證有解。

AGC044E Random Pawn

題目描述:給定 \(n\) 個排成環的自然數二元組 \((a_i,b_i)\),一開始你會等概率隨機選一個位置開始,每次操作,若你當前在 \(p\) 位置,你可以選擇結束操作並獲得 \(a_i\) 分,或者失去 \(b_i\) 分並等概率隨機選相鄰兩個位置中的一個再開始一輪操作。求期望獲得分數的最大值。

資料範圍:\(n\le 2\times 10^5,0\le a_i\le 10^{12},0\le b_i\le 100\)

發現到達最大值時一定會直接停止,所以斷環成鏈。 \(f_p=\max(a_p,\frac{f_{p-1}+f_{p+1}}{2}-b_p)\)

然後 \(b_i=0\) 時就是 Balance Beam 這道題,考慮轉化,構造 \(c_p\) 使 \(f_p+c_p=\max(a_p+c_p,\frac{(f_{p-1}+c_{p-1})+(f_{p+1}+c_{p+1})-c_{p-1}-c_{p+1}}{2}+c_p-b_p)\),要求常數項為 \(0\),即 \(c_{p+1}=2c_p-2b_p-c_{p-1}\),取 \(c_0=c_1=0\) 即可,時間複雜度 \(O(n)\)

AGC045F Division into Multiples

題目描述:給定 \(x\)\(a\)\(y\)\(b\),將它們分成若干組,求和為 \(c\) 的倍數的組的數量的最大值。\(T\) 組資料。

資料範圍:\(T\le 2\times 10^4,1\le a,x,b,y,c\le 10^9\)

AGC045E Fragile Balls

題目描述:給定 \(n\) 個盒子和 \(m\) 個球,第 \(i\) 個球在第 \(a_i\) 個盒子裡。每次你可以選擇一個包含至少兩個球的盒子,拿出一個球到另一個盒子裡。第 \(i\) 個球的移動次數不能超過 \(c_i\),求最少的移動次數使第 \(i\) 個球到第 \(b_i\) 個盒子裡。需判斷無解。

資料範圍:\(n,m\le 10^5\)

URAL2057 Non-palindromic cutting

題目描述:給定字串 \(S\),你需要將其分割成非迴文子串,求段數的最小值和最大值。

資料範圍:\(|S|\le2\times10^5\)

CF932G Palindrome Partition

題目描述:給定字串 \(S\),求將其劃分迴文的方案數。

資料範圍:\(|S|\le10^6\)

Data Structure Quiz

題目描述:給定 \(n\times n\) 的矩陣,要求支援 \(m_1\) 次矩形加,再求 \(m_2\) 次矩形最大值。

資料範圍:\(n,m_1\le 5\times 10^4,m_2\le 5\times 10^5\)

CodeChef SUMDIS

題目描述:給定 \(n\) 個點的 DAG,其中的邊僅有:

  • \((i,i+1,a_i)\),其中 \(i\in[1,n-1]\)
  • \((i,i+2,b_i)\),其中 \(i\in[1,n-2]\)
  • \((i,i+3,c_i)\),其中 \(i\in[1,n-3]\)

求兩兩最短路之和。\(T\) 組資料。

資料範圍:\(T\le 10^4,n\le 10^5,\sum n\le 3\times 10^5,a_i,b_i,c_i\le 10^4\)

考慮分治,跨過中間三個點的路徑必須經過這三個點之一。列舉經過哪一個最短,轉化為二維數點問題,排序+樹狀陣列即可,時間複雜度 \(O(n\log n)\)

TCO SemiPerfectPower

題目描述:定義半完全冪數為形如 \(a\times b^c\) 的數,其中 \(1\le a<b,1<c\)。求 \([L,R]\) 的半完全冪數的個數。

資料範圍:\(1\le L\le R\le 8\times 10^{16}\)

發現當 \(c>3\) 時,可以轉化為 \(c=2,3\) 的情況,所以只考慮 \(c=2,3\)。差分轉化為求 \([1,n]\) 的個數。

若可以表示為 \(c=2\) 的情況,由於 \(a\le \sqrt[3]{n}\),直接列舉 \(a\) 即可。

若可以表示為 \(c=3\) 而不能表示為 \(c=2\)

AGC044D Guess the Password

這是一道互動題

題目描述:互動器有一個長度不超過 \(L\) 的字串 \(S\),你可以詢問 \(Q\) 次長度不超過 \(L\) 的字串 \(T\),互動器會回答 \(S\)\(T\) 的編輯距離(操作定義為插入,刪除,替換),你需要求出 \(S\)

資料範圍:\(L=128,Q=850,|\Sigma|=62\),資料在互動開始前確定。

有一個我不知道的結論,兩個字串 \(A,B\) 的編輯距離等於 \(\max(|A|,|B|)\) 減去最長公共子序列的長度。證明考慮兩者的 dp 轉移。

所以我們可以改為查詢出最長公共子序列的長度。查詢 \(L\)\(c\) 的字串可以得到字元 \(c\) 的出現次數。還可以查詢字串 \(T\) 是否為 \(S\) 的子序列,可以考慮歸併排序的方法,將兩個字串按 \(S\) 中的順序合併,最多需要 \(|A|+|B|-1\) 次操作合併兩個字串 \(A,B\),最後得到的字串。使用啟發式合併,操作次數就是 \(O(L\log L+|\Sigma|)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int mod = 998244353, L = 128;
const char id[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n;
int query(const string &s){cout << "? " << s << endl; int x; read(x); return x;}
bool check(const string &s){return query(s) + s.size() == n;}
struct cmp {bool operator () (const string &a, const string &b){return a.size() > b.size();}};
priority_queue<string, vector<string>, cmp> pq;
int main(){
	for(Rint i = 0;i < 62;++ i){
		int tmp = L - query(string(L, id[i]));
		n += tmp; if(tmp) pq.push(string(tmp, id[i]));
	}
	while(pq.size() > 1){
		string a = pq.top(); pq.pop();
		string b = pq.top(), res; pq.pop();
		int p1 = 0, p2 = 0, l1 = a.size(), l2 = b.size();
		while(p1 < l1 && p2 < l2)
			if(check(res + a[p1] + b.substr(p2))) res += a[p1 ++];
			else res += b[p2 ++];
		if(p1 < l1) res += a.substr(p1);
		if(p2 < l2) res += b.substr(p2);
		pq.push(res);
	}
	cout << "! " << pq.top() << endl;
}

2019-2020 XX Opencup GP of Tokyo E Count Modulo 2

題目描述:給定自然數集合 \(A\) 和正整數 \(n,S\),求滿足

  • \(\sum_{i=1}^na_i=S\)
  • \(\forall i\in[1,n],a_i\in A\)

的長為 \(n\) 的序列 \(a\) 的個數\(\bmod 2\)\(T\) 組資料。

資料範圍:\(T\le 5,|A|\le 200,n,S\le 10^{18}\)\(A\) 中的元素不超過 \(v=10^5\)

\(A_i\) 取了 \(b_i\) 次,則當 \(\sum_{i=1}^nA_ib_i=S,\sum b_i=n\) 時,貢獻答案 \(\frac{n!}{\prod b_i!}\) 次。根據 Kummer Theorem,當且僅當 \(b_i\) 兩兩按位與為 \(0\) 的時候,貢獻答案 \(1\) 次。於是問題轉化為,對於 \(n\) 的所有二進位制位 \(i\),可以將 \(S\) 減去 \(2^ia_j\),求最後得到 \(0\) 的方案數。我們從高到低考慮 \(n\) 的二進位制位 \(i\),用 dp 列舉減去哪一項,由於當 \(S>2^{i+1}v\) 時一定不合法,所以可以考慮每次滾動二進位制位,將較大的一半扔掉。使用 bitset 優化轉移,時間複雜度 \(O(\frac{v|A|\log n}{w})\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int mod = 998244353;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int t, k, now, pre, a[203]; LL n, s;
bitset<200005> f[2], tmp;
int main(){
	read(t);
	while(t --){
		read(n); read(s); read(k); now = 0; pre = 1;
		for(Rint i = 0;i < k;++ i) read(a[i]);
		f[0].reset(); f[0].set(0);
		for(Rint i = 60;~i;-- i){
			swap(now, pre); tmp.reset(); f[now].reset();
			if((n >> i) & 1) for(Rint j = 0;j < k;++ j) tmp ^= f[pre] >> a[j];
			else tmp = f[pre];
			if(i) for(Rint j = 0;j <= 100000;++ j) if(tmp[j]) f[now].flip(j << 1 | ((s >> i-1) & 1));
		}
		cout << tmp[0] << endl;
	}
}

Luogu4464 [國家集訓隊]JZPKIL

題目描述:給定自然數 \(n,x,y\),求

\[\sum_{i=1}^n\gcd(i,n)^x\text{lcm}(i,n)^y\bmod(10^9+7) \]

\(T\) 組資料。

資料範圍:\(T\le 100,n\le 10^{18},0\le x,y\le 3000\)

\[\begin{aligned} Ans&=n^y\sum_{i=1}^n\gcd(i,n)^{x-y}i^y \\ &=n^y\sum_{d|n}d^x\sum_{i=1}^{\frac{n}{d}}[\gcd(i,\frac{n}{d})=1]i^y \\ &=n^y\sum_{d|n}d^x\sum_{i=1}^{\frac{n}{d}}i^y\sum_{e|\frac{n}{d},e|i}\mu(e) \\ &=n^y\sum_{d|n}d^x\sum_{e|\frac{n}{d}}e^y\mu(e)\sum_{i=1}^{\frac{n}{de}}i^y \\ &=n^yy!\sum_{d|n}d^x\sum_{e|\frac{n}{d}}e^y\mu(e)\sum_{i=1}^{y+1}\frac{B_{y+1-i}}{i!}(\frac{n}{de})^i \\ &=n^yy!\sum_{i=1}^{y+1}\frac{B_{y+1-i}}{i!}\sum_{d|n}d^x\sum_{e|\frac{n}{d}}e^y\mu(e)(\frac{n}{de})^i \end{aligned} \]

設後面的和式為 \(F_i(n)\),它可以看成三個積性函式的 Dirichlet 卷積,所以 \(F_i(n)\) 是積性函式。

\(f=\text{id}^x\times\text{id}^c\)\(F_c(1)=1\)\(F_c(p)=p^x+p^c-p^y\)\(F_c(p^k)=f(p^k)-f(p^{k-1})p^y\)

其中 \(f(p^k)=p^{kc}\sum_{i=0}^kp^{i(x-c)}\)。使用 Pollard-rho 分解質因數,直接計算。求伯努利數的時候可以暴力。

\[B_k=1-\sum_{i=0}^{k-1}\frac{B_i}{(k+1-i)!},B_0=1 \]

狄利克雷生成函式

對於一個數論函式 \(f_i\),定義它的 DGF 為 \(F(x)=\sum\limits_{i=1}^{+\infty}\frac{f_i}{i^x}\)。和 EGF 一樣,簡單的 DGF 可以用黎曼 Zeta 函式 \(\zeta(s)\) 表示。

易得 DGF 乘積對應 Dirichlet 卷積。這個東西可以證明 Mobius 反演幫助我們在面對比較複雜的 Dirichlet 卷積時快速計算。

LOJ6713「EC Final 2019」狄利克雷 k 次根 加強版

題目描述:給定 \(f_1,f_2,\dots,f_n\),求 \(g_1,g_2,\dots,g_n\) 使 Dirichlet 卷積,模質數 \(p\) 意義下 \(g^k=f\)

資料範圍:\(n\le 10^6,p=998244353\)

考慮 exp/ln 方法,我們知道 \(F'(x)=\sum(\frac{f_i}{i^x})'=\sum\frac{f_i\ln i}{i^x}\)\(\ln F(x)=\int\frac{F'(x)}{F(x)}\),求逆直接計算即可。\(\exp\) 可以直接取逆運算。時間複雜度是列舉倍數的 \(O(n\log n)\)

但有個問題,\(\ln i\)\(\mathbb{F}_p\) 下沒有定義,但其實之後在取積分的時候會去掉 \(\ln\),實際上只用到了 \(\ln\) 的完全積性和 \(\ln 1=0\),於是隨便找一個替代,比如質因子質數之和。

當然也可以用 \(f^p=\epsilon\) 得到 \(g=f^{k^{-1}\bmod p}\),使用冪函式的遞推公式可以做到同樣的複雜度。

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 1000003, mod = 998244353;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, k, a[N], b[N], pri[N], tot, c[N], inv[100];
bool notp[N];
int main(){
	read(n); read(k); notp[0] = notp[1] = true; k = ksm(k, mod - 2);
	for(Rint i = 2;i <= n;++ i){
		if(!notp[i]) pri[tot ++] = i, c[i] = 1;
		for(Rint j = 0;j < tot && i * pri[j] <= n;++ j){
			notp[i * pri[j]] = true;
			c[i * pri[j]] = c[i] + 1;
			if(!(i % pri[j])) break;
		}
	}
	inv[0] = inv[1] = 1;
	for(Rint i = 2;i < 100;++ i) inv[i] = mod - (LL) mod / i * inv[mod % i] % mod;
	for(Rint i = 1;i <= n;++ i) read(a[i]);
	for(Rint i = 1;i <= n;++ i) b[i] = (LL) a[i] * c[i] % mod;
	for(Rint i = 1;i <= n;++ i){
		for(Rint j = 2;i * j <= n;++ j)
			qmo(b[i*j] -= (LL) b[i] * a[j] % mod);
		b[i] = (LL) b[i] * inv[c[i]] % mod;
	}
	for(Rint i = 1;i <= n;++ i){b[i] = (LL) b[i] * k % mod * c[i] % mod; a[i] = 0;} a[1] = 1;
	for(Rint i = 1;i <= n;++ i){
		a[i] = (LL) a[i] * inv[c[i]] % mod;
		for(Rint j = 2;i * j <= n;++ j)
			qmo(a[i*j] += (LL) a[i] * b[j] % mod - mod);
	}
	for(Rint i = 1;i <= n;++ i) printf("%d ", a[i]);
}

AGC041F Histogram Rooks

題目描述:給定寬為 \(n\) 的 Histogram,第 \(i\) 列有 \(h_i\) 個方格,下邊界對齊。求在格子裡放一些使得所有格子都被至少一個的攻擊範圍覆蓋到的方案數\(\bmod 998244353\),這裡的不能越過空位。

資料範圍:\(1\le h_i\le n\le 400\)

注意到不能越過空位,於是想到建小根笛卡爾樹,每個節點的兒子不互相影響,符合樹形 dp 的性質。

注意到所有格子都在攻擊範圍的條件,於是想到容斥變為欽定 \(s\) 個格子不能被覆蓋,容斥係數為 \((-1)^s\)

若一個格子沒有被覆蓋,則它所在的列都沒有,於是可以設 \(dp_{s,i}\) 表示 \(s\) 結點所在區間中,有 \(i\) 列包含欽定的不被覆蓋的格子的方案數(帶容斥係數)。關於處理每一列都包含欽定的格子,考慮再次容斥,欽定 \(j\) 列不包含被欽定的格子。那麼長為 \(len\) 的一整行的貢獻就是:

  1. 沒有任何一個方格欽定沒有被覆蓋,則除欽定的不被覆蓋的列之外,可以隨便放,方案數為 \(2^{len-i}\)

  2. 有些方格欽定被覆蓋,則在欽定的不被覆蓋的列中,欽定不包含被欽定的格子的列之外的 \(i-j\) 個位置,可以選擇一些放,容斥係數為 -1 的的個數次方,即 \(\sum_{k=1}^{i-j}(-1)^k\binom{i-j}{k}=-[i>j]\)

所以當前節點中一行的貢獻就是 \(2^{len-i}-[i>j]\)。又因為 \(i\ge j\),所以我們不關心 \(j\) 的大小,只用多記一維表示 \([i=j]\) 即可。時空複雜度 \(O(n^2)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 403, mod = 998244353;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, h[N], pw2[N], ans, f[N][N], g[N][N];
int solve(int l, int r, int val){
	if(l > r) return 0;
	int mid = l;
	for(Rint i = l + 1;i <= r;++ i) if(h[mid] > h[i]) mid = i;
	int lc = solve(l, mid-1, h[mid]), rc = solve(mid+1, r, h[mid]);
	for(Rint i = 0;i <= mid-l;++ i)
		for(Rint j = 0;j <= r-mid;++ j){
			int tmp = (LL) f[lc][i] * f[rc][j] % mod;
			qmo(f[mid][i+j] += tmp - mod); qmo(f[mid][i+j+1] -= tmp); qmo(g[mid][i+j+1] += tmp - mod);
			qmo(g[mid][i+j] += (LL) (f[lc][i] + g[lc][i]) * (f[rc][j] + g[rc][j]) % mod - mod); qmo(g[mid][i+j] -= tmp);
		}
	for(Rint i = 0;i <= r-l+1;++ i){
		f[mid][i] = (LL) f[mid][i] * ksm(pw2[r-l+1-i], h[mid] - val) % mod;
		g[mid][i] = (LL) g[mid][i] * ksm(pw2[r-l+1-i]-1, h[mid] - val) % mod;
	} return mid;
}
int main(){
	read(n); pw2[0] = f[0][0] = 1;
	for(Rint i = 1;i <= n;++ i){read(h[i]); qmo(pw2[i] = (pw2[i-1]<<1) - mod);}
	int rt = solve(1, n, 0);
	for(Rint i = 0;i <= n;++ i){qmo(ans += f[rt][i] - mod); qmo(ans += g[rt][i] - mod);}
	printf("%d\n", ans);
}

UOJ504【JOISC2020】變色龍

這是一道互動題

題目描述:有 \(2n\) 只變色龍,它們的原色有 \(n\) 種,性別有 X,Y 兩種,每種顏色恰好有一個 X 性變色龍和一個 Y 性變色龍的原色是這種顏色。每一隻變色龍都 love 一隻異性變色龍,且每隻變色龍與它 love 的變色龍原色不同,且沒有兩個變色龍喜歡同一個變色龍。你可以組織至多 \(Q\) 次見面會,對於每隻參加見面會的變色龍,若它 love 的變色龍也參加了見面會,則它的顏色是它喜歡的變色龍的顏色,否則是它的原色,互動庫返回這場見面會中顏色的數量。你需要找出所有相同顏色的 \(n\) 對變色龍。

資料範圍:\(n\le 500,Q=20000\)

【解題思路 TODO】

#include<bits/stdc++.h>
#include"chameleon.h"
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef vector<int> VI;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 1003, mod = 998244353;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
VI V[2], E[N];
int col[N], suki[N];
void dfs(int x, int c){
	col[x] = c; V[c].PB(x);
	for(Rint v : E[x]) if(col[v] == -1) dfs(v, !c);
}
void add(VI vec, int x){
	random_shuffle(vec.begin(), vec.end());
	while(true){
		vec.PB(x);
		if(Query(vec) == vec.size()) return;
		vec.pop_back();
		int l = 0, r = vec.size() - 1, mid, ans = -1;
		while(l <= r){
			mid = l + r >> 1;
			VI tmp(vec.begin() + l, vec.begin() + mid + 1); tmp.PB(x);
			if(Query(tmp) == tmp.size()) l = mid + 1;
			else ans = mid, r = mid - 1;
		}
		if(ans == -1) return;
		int y = vec[ans]; E[x].PB(y); E[y].PB(x);
		vec.erase(vec.begin(), vec.begin() + ans + 1);
	}
}
void work(int x){
	V[0].clear(); V[1].clear();
	for(Rint i = 1;i < x;++ i) col[i] = -1;
	for(Rint i = 1;i < x;++ i) if(col[i] == -1) dfs(i, 0);
	add(V[0], x); add(V[1], x);
}
void Solve(int n){
	srand(time(0));
	for(Rint i = 1;i <= (n<<1);++ i) work(i);
	for(Rint i = 1;i <= (n<<1);++ i) if(E[i].size() == 3)
		for(Rint j = 0;j < 3;++ j){
			VI tmp = {i};
			for(Rint k = 0;k < 3;++ k) if(j != k) tmp.PB(E[i][k]);
			if(Query(tmp) == 1){suki[i] = E[i][j]; break;}
		}
	for(Rint i = 1;i <= (n<<1);++ i){
		if(E[i].size() == 1){if(i < E[i][0]) Answer(i, E[i][0]); continue;}
		for(Rint j = 0;j < 3;++ j){
			if(suki[i] == E[i][j] || suki[E[i][j]] == i) continue;
			if(i < E[i][j]) Answer(i, E[i][j]);
		}
	}
}

UOJ507【JOISC2020】星座3

題目描述:給定一個寬為 \(n\) 的 Histogram \(h_i\)\(m\) 個格子是特殊的,第 \(i\) 個特殊格子有權值 \(c_i\),你需要一些特殊格子,使得任何一個子矩形都至多包括一個特殊格子。求這些特殊格子的權值和的最小值。

資料範圍:\(n,m\le 2\times 10^5,1\le h_i\le n,1\le c_i\le 10^9\)

容易想到建出小根笛卡爾樹,那麼每個節點所在的區間中至多隻能有一個特殊格子。於是自下往上 dp,考慮保留哪一個特殊格子。

若保留了一個特殊格子,則它到根的路徑上的區間都不能保留特殊格子。於是若保留當前區間的特殊格子,就要將子樹內的所有特殊格子扔掉,比較兩者權值來決策即可。維護子樹和直接用 BIT,時間複雜度 \(O(n\log n)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define PB push_back
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 200003;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0; bool f = false;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
int n, m; LL ans, tr[N];
vector<int> h[N];
vector<pii> v[N];
struct UFS {
    int fa[N];
    int get(int x){return x == fa[x] ? x : fa[x] = get(fa[x]);}
} L, R;
int EI(int x){return x & -x;}
void upd(int p, LL v){for(;p <= n;p += EI(p)) tr[p] += v;}
LL qry(int p){LL t = 0; for(;p;p -= EI(p)) t += tr[p]; return t;}
int main(){
    read(n);
    for(Rint i = 1;i <= n+1;++ i) L.fa[i] = R.fa[i] = i;
    for(Rint i = 1, x;i <= n;++ i){
        read(x); h[x].PB(i);
    } read(m);
    while(m --){
        int x, y, c;
        read(x); read(y); read(c);
        v[y].PB(MP(x, c));
    }
    for(Rint i = 1;i <= n;++ i){
        LL tmp;
        for(pii b : v[i])
            if((tmp = qry(b.fi)) >= b.se) ans += b.se;
            else {
                ans += tmp;
                upd(L.get(b.fi) + 1, b.se - tmp);
                upd(R.get(b.fi), tmp - b.se);
            }
        for(Rint j : h[i]){L.fa[j] = j-1; R.fa[j] = j+1;}
    }
    printf("%lld\n", ans);
}

LOJ3081「2019 集訓隊互測 Day 5」簡單計數

題目描述:給定正整數 \(n,k\) 和自然數集合 \(S\),你需要求 \(n\) 個點的帶標號有向無環圖數量\(\bmod 998244353\),使得每個點出度 \(\le 1\),入度 \(\in S\),邊有 \(k\) 種顏色。

資料範圍:\(n\le 9\times 10^8,k\le 10^7,S\subseteq [0,3]\)

LOJ3070「2019 集訓隊互測 Day 1」最短路徑

題目描述:給定 \(n\) 個點的基環樹(無向)求 \(\sum_{u<v}\text{dis}(u,v)^k\bmod 998244353\)

資料範圍:\(n\le 10^5,k\le 10^9\)

看到 \(k\) 的範圍這麼大就不太知道如何轉化,於是考慮直接對 \(i\in[0,n)\) 求出 \(\text{dis}(u,v)=i\) 的點對數。

然後每棵子樹直接點分治+FFT,環上的貢獻考慮距離,分類討論之後同理。時間複雜度 \(O(n\log^2n)\)

-CF Gym 102129 B Associativity Degree

題目描述:給定正整數 \(n,q\),設 \(U=[1,n]\cap\Z\)\(q\) 次詢問正整數 \(k\),你要建構函式 \(f:U^2\rightarrow U\) 使得有 \(k\)\(a,b,c\in U\) 滿足 \(f(f(a,b),c)=f(a,f(b,c))\)。需判斷無解。

資料範圍:\(n\le 64,qn^2\le 10^6\)

CF Gym 102129 D Basis Change

題目描述:給定長為 \(k\) 的正整數序列 \(a,b\),考慮所有滿足 \(\forall n>k,F_n=\sum_{i=1}^ka_iF_{n-i}\) 的無窮序列 \(F\),你需要求出長為 \(k\) 的序列 \(c\),使得 \(\forall n>b_k,F_n=\sum_{i=1}^kc_iF_{n-b_i}\)。對於 \(i\in[1,k]\cap\Z\),輸出 \(c_i\bmod (10^9+7)\)

資料範圍:\(k\le 128,a_i\le 10^9,b_{i-1}<b_i\le 10^9\)

根據 \(F\) 的生成函式知道,滿足條件當且僅當 \(A(x)=(1-\sum_{i=1}^ka_ix^i)\)\((1-\sum_{i=1}^kc_ix^{b_i})\) 的因式,則可以將 \(x^{b_k}\bmod A(x)\) 計算出來,然後用高斯消元解出 \(c_i\),時間複雜度 \(O(k^3\log V)\)\(O(k^3+k^2\log k\log V)\)(FFT優化)。

CF Gym 102129 G Permutant

題目描述:對於 \(n\times n\) 的矩陣 \(A\),給定長為 \(n\) 的序列 \(a\) 長為 \(n\) 的排列 \(p\),滿足 \(A_{0,i}=a_i\)\(A_{i,j}=A_{i-1,p_j}\)。求 \((\det A)\bmod(10^9+7)\)

資料範圍:\(n\le 5000,a_i\le 10^9\)

有一個我不知道的結論,如果 \(p\) 的環數 \(\ge 2\),則答案為 \(0\)。證明可以考慮由於每個環的和一定,那麼一定存在一些\(n\) 維向量不屬於行向量組張成的線性空間,所以 \(\text{rank}(A)<n\)

剩下的情況,可以通過交換列將 \(A_{i,j}\) 變為 \(a_{(j-i)\bmod n}\)

關於迴圈矩陣,這是一個經典結論(?),構造矩陣 \(B_{i,j}=\omega_n^i\),構造多項式 \(f(x)=\sum_{i=0}^{n-1}a_ix^i\),則 \((A\times B)_{i,j}=\omega_n^{ij}f(\omega_n^j)\),則 \(|AB|=\prod_{i=0}^{n-1}f(\omega_n^{i})|B|\),所以 \(|A|=\prod_{i=0}^{n-1}f(\omega_n^i)\)

單位根存在時可以用 Chirp-Z Transform 直接多點求值,但是單位根有可能不存在。

有一個東西叫結式,也是經典東西。

定義兩個多項式 \(A(x)=a_n\prod_{i=0}^{n-1}(x-\mu_i),B(x)=b_m\prod_{i=0}^{m-1}(x-\lambda_i)\) 的結式

\[\mathcal{R}(A,B)=a_n^mb_m^n\prod_{i=0}^{n-1}\prod_{j=0}^{m-1}(\mu_i-\lambda_j)=a_n^m\prod_{i=0}^{n-1}B(\mu_i)=(-1)^{nm}b_m^n\prod_{i=0}^{m-1}A(\lambda_i) \]

易得 \(\mathcal{R}(A,B)=(-1)^{nm}\mathcal{R}(B,A)\)。設 \(C=B\bmod A\),則 \(B(\mu_i)=C(\mu_i)\),所以 \(\mathcal{R}(A,B)=(-1)^{nm}a_n^{m-\text{deg}(C)}\mathcal{R}(C,A)\),使用這個遞迴式計算,時間複雜度為 \(O(nm)\)

\(g(x)=x^n-1\),則答案就是 \(\mathcal{R}(f,g)\)。直接計算,時間複雜度 \(O(n^2)\)

LOJ3280「JOISC 2020 Day4」首都城市

題目描述:給定 \(n\) 個點的樹,將點集劃分為 \(k\) 個集合,求能選擇其中儘量少的多少個集合合併,得到一個連通塊。

資料範圍:\(k\le n\le 10^5\)

我們可以將每個集合的虛樹建出來,如果集合 \(i\) 的虛樹包含另一個集合 \(j\) 的節點,那麼連一條邊 \(i\rightarrow j\),答案就是出度為 \(0\) 的強連通分量的 \(siz\) 最小值。使用倍增優化連邊,複雜度 \(O(n\log n)\)

還有一種更高妙的連邊方法,即點 \(i\) 所在集合向它父親所在集合連邊當且僅當,\(i\) 不是它所在集合的虛樹的根。容易證明兩種建圖方法做傳遞閉包之後相等。複雜度 \(O(n)\)

#include<bits/stdc++.h>
#define Rint register int
#define PB push_back
using namespace std;
const int N = 200003;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int n, k, head[N], to[N<<1], nxt[N<<1], col[N], dfn[N], out[N], fa[N], tim, rt[N];
vector<int> E[N];
void add(int a, int b){
    static int cnt = 0;
    to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt;
}
void dfs(int x){
    dfn[x] = ++ tim;
    for(Rint i = head[x];i;i = nxt[i])
        if(to[i] != fa[x]){fa[to[i]] = x; dfs(to[i]);}
    out[x] = tim;
}
int stk[N], low[N], top, ccol[N], cnum, ans; bool ins[N];
void tarjan(int x){
    stk[++ top] = x; dfn[x] = low[x] = ++ tim; ins[x] = true; int tmp = top;
    for(Rint v : E[x])
        if(!dfn[v]){tarjan(v); chmin(low[x], low[v]);}
        else if(ins[v]) chmin(low[x], dfn[v]);
    if(dfn[x] == low[x]){
        ++ cnum; bool flag = true;
        for(Rint i = tmp;i <= top;++ i){ins[stk[i]] = false; ccol[stk[i]] = cnum;}
        for(Rint i = tmp;i <= top && flag;++ i)
            for(Rint v : E[stk[i]]) flag &= ccol[v] == cnum;
        if(flag) chmin(ans, top - tmp); top = tmp - 1;
    }
}
int main(){
    read(n); read(k); ans = k-1;
    for(Rint i = 1, u, v;i < n;++ i){
        read(u); read(v); add(u, v); add(v, u);
    } dfs(1);
    for(Rint i = 1;i <= n;++ i){
        read(col[i]); int &x = rt[col[i]];
        if(!x || dfn[x] > dfn[i]) x = i;
    }
    for(Rint i = 1;i <= n;++ i) if(dfn[i] > out[rt[col[i]]]) rt[col[i]] = 0;
    for(Rint i = 1;i <= n;++ i) if(i != rt[col[i]]) E[col[i]].PB(col[fa[i]]);
    for(Rint i = 1;i <= k;++ i) dfn[i] = 0; tim = 0;
    for(Rint i = 1;i <= k;++ i) if(!dfn[i]) tarjan(i);
    printf("%d\n", ans);
}

LOJ2734「JOISC 2016 Day 2」女裝大佬

題目描述:原題面

手玩一下就發現,不合法的情況就是某個時刻,佇列中只有男生。將女生設為 \(-1\),男生設為 \(+1\),那麼如果有後綴和 \(\ge 2\),那麼說明這個位置的男生後面的女生都**了,於是不合法。

如果將最後一個男生從末尾調到開頭,那麼中間所有人的不滿意度 \(+1\),且字尾和 \(-1\)。所以答案就是最大字尾和 \(-1\)。直接計算,時間複雜度與讀入複雜度同階。

#include<bits/stdc++.h>
#define Rint register int
using namespace std;
typedef long long LL;
const int N = 200003;
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
LL n, p[N], b[N], mp[N], tot, ans = 1, tmp; int m;
char str[N];
int main(){
    scanf("%lld%d", &n, &m);
    for(Rint i = 1;i <= m;++ i){
        scanf("%s%lld", str, b+i); int len = strlen(str);
        for(Rint j = len-1;~j;-- j){
            p[i] += str[j] == 'M' ? 1 : -1; chmax(mp[i], p[i]);
        } tot += p[i] * b[i];
    }
    if(tot > 0) return puts("-1"), 0;
    for(Rint i = m;i;-- i){
        chmax(ans, tmp + (b[i]-1) * max(0ll, p[i]) + mp[i]);
        tmp += b[i] * p[i];
    }
    printf("%lld\n", ans-1);
}

LOJ3030「JOISC 2019 Day1」考試

題目描述:給定 \(n\) 個點 \((x_i,y_i)\)\(q\) 次詢問 \(a,b,c\),求有多少個點滿足 \(x_i\ge a,y_i\ge b,x_i+y_i\ge c\)

資料範圍:\(n,q\le 10^5,0\le x_i,y_i\le 10^9\)

三維數點模板?

考慮反面,可以劃分為三個區域:\(x_i<a\and x_i+y_i\ge c\)\(y_i<b\and x_i+y_i\ge c\)\(x_i+y_i<c\)。直接計算,時間複雜度 \(O(n\log n)\)

LOJ2737「JOISC 2016 Day 3」電報

題目描述:給定長為 \(n\) 的正整數序列 \(a_i,c_i\),求 \(\sum_{i=1}^n[a_i\ne p_i]c_i\) 的最大值,其中 \(p\) 是長為 \(n\) 的排列,且若所有 \(i\)\(p_i\) 連有向邊,得到一個環。

資料範圍:\(n\le 10^5,1\le a_i\le n,a_i\ne i,1\le c_i\le 10^9\)

這裡有一個小 trick,若 \(i\rightarrow a_i\),則每個連通塊是基環內向樹。

先特判一下給定的是一個大環,那麼答案是 \(0\)。否則每個連通塊都需要去掉一些邊變成一條鏈。這個可以直接維護,時間複雜度 \(O(n)\)

#include<bits/stdc++.h>
#define Rint register int
using namespace std;
typedef long long LL;
const int N = 100003;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, a[N], vis[N]; LL ans, mv[2][N], c[N];
int main(){
    read(n);
    for(Rint i = 1;i <= n;++ i){read(a[i]); read(c[i]); ans += c[i];}
    for(Rint i = 1;i <= n;++ i) if(!vis[i]){
        int j = i;
        while(!vis[j]){vis[j] = i; j = a[j];}
        if(vis[j] == i){
            int siz = 0;
            while(~vis[j]){vis[j] = -1; j = a[j]; ++ siz;}
            if(n == siz){puts("0"); return 0;}
        }
    }
    for(Rint i = 1;i <= n;++ i){
        chmax(mv[0][a[i]], c[i]);
        if(~vis[i]) chmax(mv[1][a[i]], c[i]);
    }
    for(Rint i = 1;i <= n;++ i) ans -= mv[0][i];
    for(Rint i = 1;i <= n;++ i) if(vis[i] == -1){
        LL tmp = 1e18; int j = i;
        while(vis[j] == -1){
            chmin(tmp, mv[0][j] - mv[1][j]);
            vis[j] = 0; j = a[j];
        }
        ans += tmp;
    }
    printf("%lld\n", ans);
}

LOJ2391「JOISC 2017 Day 1」港口設施

題目描述:給定 \(n\) 個區間 \([a_i,b_i]\),求有多少種將它們劃分為兩個集合的方法,使得同一個集合裡的區間相離或包含。答案對 \(10^9+7\) 取模。

資料範圍:\(n\le 10^6,1\le a_i,b_i\le 2n\),這 \(2n\)\(a_i,b_i\) 兩兩不同。

若兩個區間相交就連邊,得到的是二分圖則答案為 \(2^{連通塊個數}\),否則為 \(0\)

連邊需要優化一下。從左到右進行掃描線,按左端點排序對區間重標號,按右端點遍歷區間,用並查集維護最左的與當前區間相交的區間。同時使用 \(nxt\) 陣列維護與當前區間可以確定在二分圖染色中異色的最右區間(顯然這些區間的標號是相鄰的),這些區間不用連邊,就可以直接 skip,於是邊數變為 \(O(n)\)。時間複雜度 \(O(n)\)

#include<bits/stdc++.h>
#define Rint register int
using namespace std;
typedef long long LL;
const int N = 1000003, mod = 1e9 + 7;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, id[N<<1], fa[N], nxt[N], vis[N], cnt, ans = 1, col[N];
vector<int> E[N];
int get(int x){return x == fa[x] ? x : fa[x] = get(fa[x]);}
void dfs(int x){
    for(Rint v : E[x])
        if(!col[v]){col[v] = 3 - col[x]; dfs(v);}
        else if(col[x] == col[v]){puts("0"); exit(0);}
}
int main(){
    read(n); fa[n+1] = nxt[n+1] = n+1;
    for(Rint i = 1, l, r;i <= n;++ i){
        read(l); read(r); id[l] = id[r] = fa[i] = nxt[i] = i;
    }
    for(Rint i = 1;i <= (n<<1);++ i)
        if(!vis[id[i]]) vis[id[i]] = ++cnt;
        else {
            int u = vis[id[i]]; fa[u] = get(u + 1);
            for(Rint v = fa[u], tmp;v <= cnt;v = tmp){
                E[u].push_back(v); E[v].push_back(u);
                tmp = get(nxt[v] + 1); nxt[v] = cnt;
            }
        }
    for(Rint i = 1;i <= n;++ i)
        if(!col[i]){col[i] = 1; dfs(i); ans *= 2; if(ans >= mod) ans -= mod;}
    printf("%d\n", ans);
}

LOJ3275「JOISC 2020 Day2」有趣的 Joitter 交友

題目描述:給定 \(n\) 個點的有向圖,\(m\) 次詢問,每次加入一條邊 \(a\rightarrow b\),詢問在 \(x\rightarrow y\rightarrow z\rightarrow y\and x\ne z\Rightarrow x\rightarrow z\) 的情況下圖的邊數。

資料範圍:\(n\le 10^5,m\le 3\times 10^5\)。保證沒有重邊和自環。

考慮將兩兩有邊的點縮成一個集合,則連向這個集合的點連向這個集合的所有點。並且若兩個集合之間通過兩條不同方向的邊相連,則可以合併成一個大集合。每個集合對答案的貢獻與它的大小和連向它的邊數有關。

使用 set 維護每個集合的入邊和出邊,加邊時若需要合併兩個集合則將小集合的所有邊加到大集合上。由於加邊時可能發生連鎖反應,可以用遞迴的方式加邊。時間複雜度 \(O(n\log^2n)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define PB push_back
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 100003, mod = 1e9 + 7;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, m, fa[N], siz[N]; set<int> in[N]; set<pii> out[N]; LL ans;
int get(int x){return x == fa[x] ? x : fa[x] = get(fa[x]);}
void add(int u, int v){
    int fu = get(u), fv = get(v);
    if(fu == fv || in[fv].count(u)) return;
    auto it = out[fv].lower_bound(MP(fu, 0));
    if(it == out[fv].end() || it->fi != fu){
        ans += siz[fv]; in[fv].insert(u); out[fu].insert(MP(fv, u)); return;
    }
    if(in[fu].size() + out[fu].size() < in[fv].size() + out[fv].size()) swap(fu, fv);
    vector<int> t1; vector<pii> t2;
    for(pii it : out[fv]){in[it.fi].erase(it.se); ans -= siz[it.fi]; t2.PB(it);}
    for(int u : in[fv]){out[get(u)].erase(MP(fv, u)); t1.PB(u);}
    ans += (2ll * siz[fu] + in[fu].size() - in[fv].size()) * siz[fv];
    out[fv].clear(); in[fv].clear();
    siz[fu] += siz[fv]; fa[fv] = fu;
    for(int u : t1) add(u, fu);
    for(pii it : t2) add(it.se, it.fi);
}
int main(){
    read(n); read(m);
    for(Rint i = 1;i <= n;++ i){fa[i] = i; siz[i] = 1;}
    for(Rint i = 1, u, v;i <= m;++ i){
        read(u); read(v); add(u, v); printf("%lld\n", ans);
    }
}

LOJ3033「JOISC 2019 Day2」兩個天線

題目描述:給定三個長為 \(n\) 的正整數序列 \(h,a,b\)\(q\) 次詢問 \(l,r\),求滿足 \(l\le i,j\le r\)\(|i-j|\in[\max(a_i,a_j),\min(b_i,b_j)]\)\((i,j)\)\(|h_i-h_j|\) 的最大值。

資料範圍:\(n,q\le 2\times 10^5,a_i\le b_i<n,h_i\le 10^9\)

一開始把區間交看成區間並了...就離譜(

按照 \(r\) 掃描線,可以固定 \(j=r\) 然後計算答案的時候求字尾 \(\max\)

\(h_i\) 有用的 \(r\) 是一個區間,對 \(r\) 有用的 \(h_i\) 也是一個區間,用維護歷史最值的線段樹即可。時間複雜度 \(O(n\log n)\)

#include<bits/stdc++.h>
#define Rint register int
#define fi first
#define se second
#define PB push_back
#define MP make_pair
using namespace std;
typedef vector<int> vi;
typedef pair<int, int> pii;
const int N = 200003, INF = 0x3f3f3f3f;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0; bool f = false;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int n, m, h[N], a[N], b[N], mx[N<<2], tag[N<<2], omx[N<<2], ans[N]; vi st[N], ed[N];
struct Node {
    int l, r, id;
    Node(int _l = 0, int _r = 0, int _id = 0): l(_l), r(_r), id(_id){}
    bool operator < (const Node &o) const {
        if(r != o.r) return r < o.r;
        if(l != o.l) return l < o.l;
        return id < o.id;
    }
} q[N];
void pushup(int x){mx[x] = max(mx[x<<1], mx[x<<1|1]); omx[x] = max(omx[x<<1], omx[x<<1|1]);}
void pushtag(int x, int v){chmax(tag[x], v); chmax(omx[x], mx[x] + tag[x]);}
void pushdown(int x){if(tag[x] != -INF){pushtag(x<<1, tag[x]); pushtag(x<<1|1, tag[x]); tag[x] = -INF;}}
void upd(int x, int L, int R, int l, int r, int v){
    if(l <= L && R <= r){pushtag(x, v); return;}
    int mid = L + R >> 1; pushdown(x);
    if(l <= mid) upd(x<<1, L, mid, l, r, v);
    if(mid < r) upd(x<<1|1, mid+1, R, l, r, v);
    pushup(x);
}
void cle(int x, int L, int R, int p, int v){
    if(L == R){mx[x] = v; tag[x] = -INF; return;}
    int mid = L + R >> 1; pushdown(x);
    if(p <= mid) cle(x<<1, L, mid, p, v);
    else cle(x<<1|1, mid+1, R, p, v);
    pushup(x);
}
int qry(int x, int L, int R, int l, int r){
    if(l <= L && R <= r) return omx[x];
    int mid = L + R >> 1, res = -INF; pushdown(x);
    if(l <= mid) res = qry(x<<1, L, mid, l, r);
    if(mid < r) chmax(res, qry(x<<1|1, mid+1, R, l, r));
    return res;
}
void solve(){
    int cur = 1;
    for(Rint i = 1;i <= n;++ i){
        for(Rint p : st[i]) cle(1, 1, n, p, h[p]);
        for(Rint p : ed[i]) cle(1, 1, n, p, -INF);
        if(i > a[i]) upd(1, 1, n, max(i - b[i], 1), i - a[i], -h[i]);
        for(;cur <= m && q[cur].r <= i;++ cur) chmax(ans[q[cur].id], qry(1, 1, n, q[cur].l, q[cur].r));
    }
}
int main(){
    read(n); memset(ans, -1, sizeof ans);
    for(Rint i = 1;i <= n;++ i){
        read(h[i]); read(a[i]); read(b[i]);
        if(i+a[i]<=n) st[i+a[i]].PB(i);
        if(i+b[i]+1<=n) ed[i+b[i]+1].PB(i);
    } read(m);
    for(Rint i = 1;i <= m;++ i){read(q[i].l); read(q[i].r); q[i].id = i;}
    sort(q + 1, q + m + 1);
    memset(omx, -0x3f, sizeof omx); memset(mx, -0x3f, sizeof mx); memset(tag, -0x3f, sizeof tag);
    solve(); for(Rint i = 1;i <= n;++ i) h[i] = INF - h[i];
    memset(omx, -0x3f, sizeof omx); memset(mx, -0x3f, sizeof mx); memset(tag, -0x3f, sizeof tag);
    solve(); for(Rint i = 1;i <= m;++ i) printf("%d\n", ans[i]);
}

LOJ3041「JOISC 2019 Day4」礦物

這是一道互動題

題目描述:給定 \(2n\) 個元素,有 \(n\) 種,每種有 \(2\) 個。初始有集合 \(S=\varnothing\),每次你可以往裡面增加一個元素或刪除一個元素,然後詢問 \(S\) 中有多少種元素。求所有相同種的元素對。

資料範圍:\(n\le 43000\),操作次數 \(\le 10^6\)

注意到一次詢問的資訊是 1bit,得到的資訊是當前加入/刪除的元素的另一個同種元素是否在集合內。

於是按順序詢問每一個數可以將元素分為兩組,每一組之間沒有同種元素。考慮使用排序的方法將一組的元素與另一組按順序對應。

設兩組為 \(a,b\),考慮分治,首先詢問左半邊中的 \(a\) 元素,然後詢問整個區間的 \(b\) 元素,就可以知道每個 \(b_i\) 所對應的同種元素在 \(a\) 的左半邊還是右半邊。然後即可遞迴下去。

若分治時平均分為兩段,則只能拿到 90 分(\(\frac{3}{2}n\log_2n+2n\)),但注意到操作次數是 左半邊長度+總長。於是考慮不平均分段。設 \(T(n)\) 為操作次數\(/n\),不妨設 \(T(n)=T(xn)+T((1-x)n)+x+1=k\log n+O(1)\)

\[\begin{aligned} k\log n&=xk\log xn+(1-x)k\log(1-x)n+x+1 \\ k\log n&=xk\log n+(1-x)k\log n+xk\log x+(1-x)k\log(1-x)+x+1 \\ -x-1&=xk\log x+(1-x)k\log (1-x) \\ k=f(x)&=-\frac{x+1}{x\log x+(1-x)\log(1-x)} \end{aligned} \]

\(f'(x)=0\)\(\log x=2\log(1-x)\),於是極值點僅有 \(x=\frac{3-\sqrt 5}{2}\),就可以拿到 100 分(\(1.44n\log_2n+2n\)

#include<bits/stdc++.h>
#include"minerals.h"
#define Rint register int
using namespace std;
const int N = 43003;
const double p = (3 - sqrt(5)) / 2;
namespace {
int a[N], b[N], c[N];
bool ask(int x){
    static int lst = 0;
    int now = Query(x); bool res = now ^ lst;
    lst = now; return res;
}
void work(int l, int r, bool o){
    if(l >= r) return;
    int mid = l + max((int)((r-l+1) * p) - 1, 0), t1 = l-1, t2 = mid;
    for(Rint i = l;i <= mid;++ i) ask(a[i]);
    for(Rint i = l;i <= r;++ i)
        if(t1 == mid) c[++ t2] = b[i];
        else if(t2 == r) c[++ t1] = b[i];
        else if(o ^ ask(b[i])) c[++ t2] = b[i];
        else c[++ t1] = b[i];
    for(Rint i = l;i <= r;++ i) b[i] = c[i];
    work(l, mid, !o); work(mid+1, r, o);
}
void solve(int n){
    int t1 = 0, t2 = 0;
    for(Rint i = 1;i <= (n<<1);++ i){
        if(ask(i)) a[++ t1] = i; else b[++ t2] = i;
    }
    work(1, n, 1);
    for(Rint i = 1;i <= n;++ i) Answer(a[i], b[i]);
}}
void Solve(int n){solve(n);}

LOJ3032「JOISC 2019 Day1」饢

題目描述:給定長為 \(l\text{cm}\) 的饢,第 \(i\text{cm}\) 是第 \(i\) 種風味的,有 \(n\) 個人分饢,第 \(i\) 個人吃長為 \(x\text{cm}\) 的風味為 \(j\) 的饢可以獲得 \(xv_{i,j}\) 的幸福度,你需要構造一種將饢切成 \(n\) 段然後任意分配給這 \(n\) 個人的方案,使得每個人獲得的幸福度不小於他獨吞整個饢所獲得幸福度的 \(\frac{1}{n}\)

資料範圍:\(n,l\le 2000,v_{i,j}\le 10^5\)

預處理出每個人將饢分為 \(n\) 段幸福度相等的斷點。每次找到還沒有被分饢的人中,幸福度最小的斷點,切給那個人即可,因為這一段的左端點必定不大於當前人的左端點。時間複雜度 \(O(nl)\)被打爆了

LOJ3036「JOISC 2019 Day3」指定城市

題目描述:給定 \(n\) 個點的樹,每條邊要建設雙向車道,各有權值。\(q\) 次詢問正整數 \(m\),你可以指定 \(m\) 個點為特殊點,求滿足 所有特殊點都在以 \(b\) 為根 \(a\) 的子樹內 的車道 \(a\rightarrow b\) 的權值之和的最小值。

資料範圍:\(n\le 2\times 10^5\)

根據 pb 的做法,可以直接上樹形 dp: \(dp_{x,i}\) 表示 \(x\) 子樹中選 \(i\) 個,且子樹外至少有一個特殊點,子樹內的邊的貢獻最小值。發現這個東西是凸的,於是可以用可並堆維護差分。統計答案的時候點分治,欽定根必須在虛樹中,然後這麼做即可。時間複雜度 \(O(n\log^2n)\)

根據神奇結論,設 \(m=i\) 時選定的點集為 \(S_i\),則當 \(i\ge 2\) 時,\(S_i\subseteq S_{i+1}\)。這個結論其實跟上面的 dp 值的凸性等價(取的最大差分就是貪心選擇使代價最大的點),由於根需要特判,所以 \(i\ge 2\) 時才正確。

「JOISC 2019 Day2」Two Transportations

這是一道通訊題

題目描述:給定兩個 \(n\) 個點的無向圖,邊帶權,Alice 和 Bob 分別知道一張圖,需要讓 Alice 求出 \(0\) 出發到所有點的最短路。兩人之間可以互發 \(29n\) 個 bit 的資訊。

資料範圍:\(n\le 2000,m\le 5\times 10^5,w_i\le 500\)

顯然是不能傳送邊的資訊的,於是考慮兩人分別跑最短路,並不斷更新。使用 Dijkstra,兩人每次給出最小的 \(dis\),然後用較小的更新較大的,再做鬆弛操作。

首先,傳送 \(dis\) 的時候,由於每次都不超過上次+邊權,於是只需要 \(9\) 個 bit。傳送一個頂點需要 \(11\) 個 bit。於是考慮 \(A,B\) 先互相傳送最小 \(dis\) 減上一次的,然後其中較小的向另一個傳送頂點編號。

#include"Azer.h"
#include<bits/stdc++.h>
#define Rint register int
#define fi first
#define se second
#define MP make_pair
using namespace std;
typedef vector<int> vi;
typedef pair<int, int> pii;
namespace {
const int N = 2003, M = 1000003;
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, cnt = -1, head[N], to[M], nxt[M], w[M], dis[N], mxdis, ans, val, nod; bool vis[N];
pii find(){
    pii res = MP(mxdis + 501, 0);
    for(Rint i = 0;i < n;++ i) if(!vis[i]) chmin(res, MP(dis[i], i));
    res.fi -= mxdis; return res;
}
void add(int a, int b, int c){
    static int cnt = 0;
    to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt; w[cnt] = c;
}
void upd(int u, int val){
    mxdis = dis[u] = val; vis[u] = true;
    for(Rint i = head[u];i;i = nxt[i]) chmin(dis[to[i]], val + w[i]);
}}
void InitA(int n, int m, vi u, vi v, vi c){
    ::n = n; memset(dis, 0x3f, sizeof dis);
    for(Rint i = 0;i < m;++ i){add(u[i], v[i], c[i]); add(v[i], u[i], c[i]);}
    upd(0, 0);
}
void ReceiveA(bool x){
    do {
        if(ans == n-1) return;
        if(cnt == -1){
            pii tmp = find(); cnt = 0;
            for(Rint i = 0;i < 9;++ i) SendA(tmp.fi >> i & 1);
        } else if(cnt < 9){
            val |= x<<cnt; ++ cnt;
            if(cnt == 9){
                pii tmp = find();
                if(val > tmp.fi){
                    for(Rint i = 0;i < 11;++ i) SendA(tmp.se >> i & 1);
                    upd(tmp.se, tmp.fi + mxdis); ++ ans; cnt = -1; val = 0;
                } else ++ cnt;
            }
        } else {
            nod |= x<<cnt-10; ++ cnt;
            if(cnt == 21){
                upd(nod, mxdis + val); nod = val = 0; cnt = -1; ++ ans;
            }
        }
    } while(cnt == -1);
}
vi Answer(){
    vi res(n);
    for(Rint i = 0;i < n;++ i) res[i] = dis[i];
    return res;
}
#include"Baijan.h"
#include<bits/stdc++.h>
#define Rint register int
#define fi first
#define se second
#define MP make_pair
using namespace std;
typedef vector<int> vi;
typedef pair<int, int> pii;
namespace {
const int N = 2003, M = 1000003;
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, cnt, head[N], to[M], nxt[M], w[M], dis[N], mxdis, val, nod; bool vis[N];
pii find(){
    pii res = MP(mxdis + 501, 0);
    for(Rint i = 0;i < n;++ i) if(!vis[i]) chmin(res, MP(dis[i], i));
    res.fi -= mxdis; return res;
}
void add(int a, int b, int c){
    static int cnt = 0;
    to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt; w[cnt] = c;
}
void upd(int u, int val){
    mxdis = dis[u] = val; vis[u] = true;
    for(Rint i = head[u];i;i = nxt[i]) chmin(dis[to[i]], val + w[i]);
}}
void InitB(int n, int m, vi u, vi v, vi c){
    ::n = n; memset(dis, 0x3f, sizeof dis);
    for(Rint i = 0;i < m;++ i){add(u[i], v[i], c[i]); add(v[i], u[i], c[i]);}
    upd(0, 0); SendB(true);
}
void ReceiveB(bool x){
    if(cnt < 9){
        val |= x<<cnt; ++ cnt;
        if(cnt == 9){
            pii tmp = find();
            for(Rint i = 0;i < 9;++ i) SendB(tmp.fi >> i & 1);
            if(val >= tmp.fi){
                for(Rint i = 0;i < 11;++ i) SendB(tmp.se >> i & 1);
                upd(tmp.se, tmp.fi + mxdis); cnt = val = 0;
            } else ++ cnt;
        }
    } else {
        nod |= x<<cnt-10; ++ cnt;
        if(cnt == 21){upd(nod, val + mxdis); cnt = val = nod = 0;}
    }
}

LOJ3039「JOISC 2019 Day4」蛋糕拼接 3

題目描述:給定 \(n\) 個正整數 pair \((v_i,c_i)\) 和正整數 \(m\),求所有選擇 \(m\) 個二元組排成環的方案中,\(\sum v_i-\sum|c_i-c_{i+1}|\) 的最大值。

資料範圍:\(3\le m\le n\le 2\times 10^5,v_i,c_i\le 10^9\)

首先按照 \(c_i\) 排個序,那麼 \(\sum |c_i-c_{i+1}|=2(\max c-\min c)\)。列舉 \([l,r]\) 表示最值,貢獻為 \(v\) 的前 \(m\) 大之和減去 \(2(c_r-c_l)\),它顯然滿足四邊形不等式,於是可以使用決策單調性優化。用主席樹維護,時間複雜度 \(O(n\log^2n)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 200003, M = N << 5;
template<typename T>
inline void read(T &x){
    int ch = getchar(); bool f = false; x = 0;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int n, m, val[N], len, rt[N], tot, siz[M], ls[M], rs[M];
LL sum[M], ans = -1e18; pii a[N];
void upd(int &x, int L, int R, int pos){
    ls[++tot] = ls[x]; rs[tot] = rs[x];
    sum[tot] = sum[x] + val[pos]; siz[tot] = siz[x] + 1; x = tot;
    if(L == R) return;
    int mid = L + R >> 1;
    if(pos <= mid) upd(ls[x], L, mid, pos); else upd(rs[x], mid+1, R, pos);
}
LL query(int x, int y, int L, int R, int k){
    if(L == R) return (LL) k * val[L];
    int mid = L + R >> 1, tmp = siz[rs[y]] - siz[rs[x]];
    if(k <= tmp) return query(rs[x], rs[y], mid+1, R, k);
    return sum[rs[y]] - sum[rs[x]] + query(ls[x], ls[y], L, mid, k - tmp);
}
void solve(int l, int r, int L, int R){
    int mid = l + r >> 1, pos = -1; LL tmp = -1e18;
    for(Rint i = max(L, mid+m-1);i <= R;++ i)
        if(chmax(tmp, query(rt[mid-1], rt[i], 0, len-1, m) - (a[i].fi - a[mid].fi << 1))) pos = i;
    chmax(ans, tmp);
    if(l < mid) solve(l, mid-1, L, pos);
    if(mid < r) solve(mid+1, r, pos, R);
}
int main(){
    read(n); read(m);
    for(Rint i = 0;i < n;++ i){read(a[i].se); read(a[i].fi); val[i] = a[i].se;}
    sort(a, a + n); sort(val, val + n); len = unique(val, val + n) - val;
    for(Rint i = 0;i < n;++ i){
        a[i].se = lower_bound(val, val + len, a[i].se) - val;
        if(i) rt[i] = rt[i-1]; upd(rt[i], 0, len-1, a[i].se);
    }
    solve(0, n-m, 0, n-1); printf("%lld\n", ans);
}

LOJ3281「JOISC 2020 Day4」傳奇糰子師傅

這是一道提交答案題

題目描述:給定 \(n\times m\) 的方格,每個格子有 \(3\) 種顏色(\(\text{A,B,C}\)),你每次可以取出 \(3\) 個方格,這些方格必須是在行或列或對角線上連續,且順序為 \(\text{ABC}\)\(\text{CBA}\),求可以取出多少次,並構造方案。

LOJ3278「JOISC 2020 Day3」收穫

題目描述:IOI 莊園有 \(n\) 個員工,\(m\) 棵蘋果樹種在湖岸。湖的周長為 \(l\) 米。

一開始員工 \(i\) 位於從湖的最北端向順時針方向前進 \(a_i\) 米處,蘋果樹 \(j\) 生長在從湖的最北端向順時針方向前進 \(b_j\) 米處。

每棵蘋果樹最多長一個蘋果,收穫後 \(c\) 秒會長出一個新的。時刻 \(0\) 時,所有的蘋果樹上都有一個蘋果。員工從時刻 開始從各自的地點以 \(1\text{m/s}\) 的速度順時針前進,遇到成熟的蘋果就將其摘下(若到達時剛長出蘋果,也要摘下),摘蘋果的時間忽略不計。

現給出 個詢問,第 \(k\) 次詢問員工 \(v_k\) 在時刻 \(t_k\) 結束時一共收穫到幾個蘋果。

資料範圍:\(1\le n,m\le 2\times 10^5,0\le a_i<a_{i+1}<l,0\le b_i<b_{i+1}<l,1\le c\le 10^9,n+m\le l\le 10^9,1\le t_k\le 10^{18}\)

第一步就沒想到?

考慮從人順時針運動變為樹逆時針運動。

這個採摘果樹的過程可以建圖!

將每個果樹連向逆時針遇到的第一個人,表示被這個人第一次採摘。

將每個人連向第一個逆時針距離他 \(\ge C\) 的人,表示若這個人採摘一次,下一次由那個人採摘。

由於所有點出度為 \(1\),所以形成一個基環內向樹,葉子表示樹的初始位置。詢問就變為了每棵樹從葉子開始運動,經過 \(t_k\) 時刻之後節點 \(v_k\) 被經過多少次。

便於處理,我們先不考慮環上的一條邊,變成一棵樹,分類討論果樹是否經過環上的特殊邊...

LOJ3034「JOISC 2019 Day2」兩道料理

題目描述:廚師比太郎正在參加一個廚藝比賽。在這場比賽中參賽者要烹飪兩道料理:IOI 蓋飯和 JOI 咖哩。

IOI 蓋飯的烹飪過程中需要 \(n\) 個步驟。第 \(i\) 步的用時是 \(a_i\) 分鐘,想要進行第 \(i\) 步的條件是已經完成了第 \(i-1\) 步。

JOI 咖哩的烹飪過程中需要 \(m\) 個步驟。第 \(i\) 步的用時是 \(b_i\) 分鐘,想要進行第 \(i\) 步的條件是已經完成了第 \(i-1\) 步。

做料理過程中需要專心致志,所以當他開始進行一個步驟時,就不能中斷。當完成了一個步驟,他也可以選擇進行另一道料理的下一個步驟。比賽開始後,在兩道料理都完成之前,他不能停下來休息。

在這場比賽中,參賽者會按照接下來的方式得到藝術感的打分:

  • 如果他在比賽的前 \(s_i\) 分鐘內完成了 IOI 蓋飯的第 \(i\) 個步驟,那麼從中他會得到 \(p_i\) 點的分數。
  • 如果他在比賽的前 \(t_i\) 分鐘內完成了 JOI 咖哩的第 \(i\) 個步驟,那麼從中他會得到 \(q_i\) 點的分數。

請你幫助比太郎設計做料理過程,最大化他做料理的藝術感評分。

資料範圍:\(n,m\le 10^6,a_i,b_i\le 10^9,s_i,t_i\le 2\times 10^{15},|p_i|,|q_i|\le 10^9\)

預處理出 \(x_i\) 表示在 \(s_i\) 分鐘內完成 IOI 蓋飯的第 \(i\) 個步驟時,至多能完成 JOI 咖哩的 \(x_i\) 個步驟。\(y_j\) 同理。

那麼就變成了從 \((0,0)\)\((m,n)\) 的一條向上/右的折線,若 \((x_i,i)\) 在折線上或下方,獲得 \(p_i\) 的分數,若 \((j,y_j)\) 在折線上或上方,獲得 \(q_j\) 的分數。

對第二種反面考慮,先將答案加上 \(q_j\),變為若 \((j-1,y_j+1)\) 在折線上或下方,獲得 \(-q_j\) 的分數。

這個顯然可以用 dp 直接做,\(dp_{i,j}=\max(dp_{i,j-1},dp_{i-1,j}+sum_{i-1,j})\)

使用 set 維護差分陣列,時間複雜度 \(O((n+m)\log n)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define PB push_back
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 1000003;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0; bool f = false;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
int n, m, a[N], p[N], b[N], q[N], x[N], y[N]; LL sa[N], sb[N], s[N], t[N], ans, dp[N];
set<int> S; vector<pii> vec[N];
int main(){
    read(n); read(m);
    for(Rint i = 1;i <= n;++ i){read(a[i]); read(s[i]); read(p[i]); s[i] -= sa[i] = sa[i-1] + a[i];}
    for(Rint i = 1;i <= m;++ i){read(b[i]); read(t[i]); read(q[i]); t[i] -= sb[i] = sb[i-1] + b[i]; if(t[i] >= 0) ans += q[i];}
    for(Rint i = 1;i <= n;++ i)if(s[i] >= 0){x[i] = upper_bound(sb + 1, sb + m + 1, s[i]) - sb - 1; if(x[i] == m) ans += p[i]; else vec[x[i]].PB(MP(i, p[i]));}
    for(Rint i = 1;i <= m;++ i)if(t[i] >= 0){y[i] = upper_bound(sa + 1, sa + n + 1, t[i]) - sa - 1; if(y[i] < n) vec[i-1].PB(MP(y[i]+1, -q[i]));}
    for(Rint i = 0;i < m;++ i){
        for(pii p : vec[i]){dp[p.fi] += p.se; S.insert(p.fi);}
        for(pii p : vec[i]){
            auto l = S.lower_bound(p.fi), r = l; LL tmp = 0;
            while(r != S.end()){
                if((dp[*r] += tmp) > 0) break;
                tmp = dp[*r]; dp[*r++] = 0;
            } S.erase(l, r);
        }
    }
    for(Rint i = 1;i <= n;++ i) ans += dp[i];
    printf("%lld\n", ans);
}

LOJ3256「JOI 2020 Final」火災

題目描述:給定長為 \(n\) 的正整數序列 \(a_i\)\(q\) 次詢問 \(t,l,r\),求

\[\sum_{i=l}^r\max_{j=\max(1,i-t)}^{i}a_j \]

資料範圍:\(n,q\le 2\times 10^5,a_i\le 10^9\)

看到區間求 \(\max\),想到用笛卡爾樹維護。對每個 \(i\) 求出左/右第一個大於它的點 \(l_i,r_i\),考慮 \(a_{l_i}\)\([i,r_i)\) 的影響。

建立平面直角座標系,設 \((x,y)\) 上的值為 \(\max\limits_{i=x-y}^xa_i\),則 \(\{(x,y)|x\in[i,r_i),y\ge x-l_i\}\)\(a_{l_i}\)\(\max\)

不知為何,若從下向上更新,則每次取 \(\max\) 的部分值都相等。可以變為區域+。

拆分為直角三角形:\(\{(x,y)|x\ge a,y\ge x-b\}\),它對詢問 \(\sum(\le p,t)\) 的貢獻次數為 \(\min(t+b,p)-\min(p,a-1)\)。由於詢問是字首差分,所以可以均減去 \(t\),變為 \(\min(b,p-t)-\min(a-1,p)\)

維護 \(\min(x,y)\) 這種東西,其中 \(x\) 只與修改有關,\(y\) 只與詢問有關,可以使用一個字首+,求字首和的資料結構來做。上樹狀陣列即可,時間複雜度 \(O((n+q)\log n)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 200003;
int n, m, a[N], l[N], r[N], st[N], tp; LL sum[N], ans[N];
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0; bool f = false;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
struct Query {
    int l, r, id;
    Query(int l = 0, int r = 0, int id = 0): l(l), r(r), id(id){}
};
vector<Query> q[N];
vector<pii> vec[N];
int EI(int x){return x & -x;}
struct BIT {
    LL a[N<<1];
    void upd(int p, LL v){for(;p <= (n<<1);p += EI(p)) a[p] += v;}
    LL qry(int p){LL res = 0; for(;p;p -= EI(p)) res += a[p]; return res;}
};
struct BBIT {
    BIT s[2];
    void upd(int p, int v){p += n; s[0].upd(1, v); s[0].upd(p+1, -v); s[1].upd(p+1, (LL)v*p);}
    LL qry(int p){p += n; return s[0].qry(p) * p + s[1].qry(p);}
} bt[2];
int main(){
    read(n); read(m);
    for(Rint i = 1;i <= n;++ i){read(a[i]); sum[i] = sum[i-1] + a[i];}
    for(Rint i = 1, t, l, r;i <= m;++ i){read(t); read(l); read(r); --l; ans[i] = sum[r] - sum[l]; q[t].PB(Query(l, r, i));}
    for(Rint i = 1;i <= n;++ i){
        while(tp && a[i] >= a[st[tp]]) r[st[tp--]] = i;
        l[i] = st[tp]; st[++tp] = i;
    }
    for(Rint i = 1;i <= n;++ i) if(l[i]){
        vec[i - l[i]].PB(MP(i, a[l[i]] - a[i]));
        if(r[i]) vec[r[i] - l[i]].PB(MP(r[i], a[i] - a[l[i]]));
    }
    for(Rint i = 1;i <= n;++ i){
        for(pii t : vec[i]){bt[0].upd(t.fi - i, t.se); bt[1].upd(t.fi - 1, t.se);}
        for(Query t : q[i]) ans[t.id] += bt[0].qry(t.r - i) - bt[0].qry(t.l - i) + bt[1].qry(t.l) - bt[1].qry(t.r);
    }
    for(Rint i = 1;i <= m;++ i) printf("%lld\n", ans[i]);
}

LOJ2350「JOI 2018 Final」月票購買

題目描述:給定 \(n\) 個點 \(m\) 條邊和四個頂點 \(s,t,u,v\),邊帶權,你可以選擇一條 \(s\)\(t\) 的最短路,將這條路徑上的邊權變為 \(0\),求 \(dis(u,v)\) 最小值。

資料範圍:\(n\le 10^5,m\le 2\times 10^5\),權值 \(\in [1,10^9]\)

第一個包都不會,人都傻了

有一個結論,\(u\)\(v\) 的最短路經過的免費邊一定是連續的。

建出 \(s\)\(t\) 的最短路徑 DAG,求出 \(f_i\)\(t_i\) 表示只經過最短路徑 DAG 上的邊,能到達的所有點 \(j\)\(dis(u,j)\)\(dis(j,v)\) 的最小值,就能直接計算答案。時間複雜度 \(O(m\log m)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<LL, int> pii;
const int N = 100003, M = N<<2;
int n, m, s, t, u, v, cnt = 1, head[N], to[M], nxt[M], w[M];
vector<int> E[N], ord;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0; bool f = false;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
void add(int a, int b, int c){to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt; w[cnt] = c;}
LL dis[4][N], f[2][N], ans; bool vis[N];
priority_queue<pii, vector<pii>, greater<pii> > pq;
void solve(int S, LL *dis){
    for(Rint i = 1;i <= n;++ i){dis[i] = 1e18; vis[i] = false;}
    dis[S] = 0; pq.push(MP(0, S));
    while(!pq.empty()){
        int now = pq.top().se; pq.pop();
        if(vis[now]) continue; vis[now] = true;
        for(Rint i = head[now];i;i = nxt[i])
            if(chmin(dis[to[i]], dis[now] + w[i])) pq.push(MP(dis[to[i]], to[i]));
    }
}
void dfs(int x){
    if(vis[x]) return; vis[x] = true;
    for(Rint v : E[x]) dfs(v); ord.PB(x);
}
int main(){
    read(n); read(m); read(s); read(t); read(u); read(v);
    for(Rint i = 1, a, b, c;i <= m;++ i){
        read(a); read(b); read(c); add(a, b, c); add(b, a, c);
    }
    solve(s, dis[0]); solve(t, dis[1]); solve(u, dis[2]); solve(v, dis[3]);
    for(Rint i = 2;i <= cnt;++ i)
        if(dis[0][to[i^1]] + w[i] + dis[1][to[i]] == dis[0][t]) E[to[i^1]].PB(to[i]);
    memset(vis, 0, sizeof vis); dfs(s); ans = dis[2][v];
    for(Rint a : ord){
        f[0][a] = dis[2][a]; f[1][a] = dis[3][a];
        for(Rint b : E[a]){chmin(f[0][a], f[0][b]); chmin(f[1][a], f[1][b]);}
        chmin(ans, min(f[0][a] + dis[3][a], f[1][a] + dis[2][a]));
    }
    printf("%lld\n", ans);
}

LOJ2344「JOI 2016 Final」鐵路票價

題目描述:給定 \(n\) 個點 \(m\) 條邊的無向簡單連通圖,初始邊權都為 \(1\)\(q\) 次詢問一條邊,將它的長度由 \(1\) 改為 \(2\),然後求與原圖相比,有多少節點到 \(1\) 的最短路長度改變了。

資料範圍:\(n\le 10^5,m\le 2\times 10^5\),保證每次詢問的邊不同。

先求出以 \(1\) 為根的最短路 DAG,然後每次刪掉一條邊 \((u,v)\),若此時點 \(x\)\(1\) 不連通了,則 \(x\) 會改變最短路長度。

維護這個東西,可以通過維護每個點的入度,每次刪邊使 \(v\) 的入度為 \(0\) 則把 \(v\) 刪掉,將 \(v\) 連出的邊也都刪掉。

注意在刪邊之前判斷這條邊是否已經被刪過。

#include<bits/stdc++.h>
#define Rint register int
using namespace std;
const int N = 100003, M = N<<2;
int n, m, Q, cnt, a[M], b[M], head[N], to[M], nxt[M], q[N], dep[N], in[N], f, r, ans;
bool vis[M];
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0; bool f = false;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
inline void add(int a, int b){to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt;}
void work(int x){
    ++ ans;
    for(Rint i = head[x];i;i = nxt[i])
        if(!vis[i+1>>1] && dep[to[i]] == dep[x] + 1){
            vis[i+1>>1] = true;
            if(!--in[to[i]]) work(to[i]);
        }
}
int main(){
    read(n); read(m); read(Q);
    for(Rint i = 1;i <= m;++ i){
        read(a[i]); read(b[i]); add(a[i], b[i]); add(b[i], a[i]);
    }
    dep[q[0] = r = 1] = 1;
    while(f < r){
        int x = q[f ++];
        for(Rint i = head[x];i;i = nxt[i])
            if(!dep[to[i]]){dep[to[i]] = dep[x] + 1; q[r++] = to[i];}
    }
    for(Rint i = 1;i <= n;++ i)
        for(Rint j = head[i];j;j = nxt[j]) if(dep[to[j]] == dep[i] + 1) ++ in[to[j]];
    while(Q --){
        int t; read(t);
        if(vis[t]){printf("%d\n", ans); continue;} vis[t] = true;
        if(dep[a[t]] > dep[b[t]]) swap(a[t], b[t]);
        if(dep[a[t]] < dep[b[t]] && !--in[b[t]]) work(b[t]);
        printf("%d\n", ans);
    }
}

LOJ3013「JOI 2019 Final」硬幣收藏

題目描述:給定平面上 \(2n\) 個硬幣 \((x_i,y_i)\),你每次可以將硬幣向四方向移動一個單位長度。你要將其移動到 \(x\in[1,n]\cap\Z,y\in\{1,2\}\)\((x,y)\) 上。過程中允許兩個硬幣在同一個點上,求最小移動次數。

資料範圍:\(n\le 10^5,|x_i|,|y_i|\le 10^9\)

有一堆結論:

  1. 可以將硬幣先移到 \(\{1,2\}\times([1,n]\cap\Z)\) 中,顯然不劣。
  2. 考慮硬幣和目標位置的匹配,移動目標位置和移動硬幣顯然是一樣的。

然後考慮從左向右貪心,先將同一位置的匹配了,然後匹配上下之間,剩下的向右移動。時空複雜度 \(O(n)\)

#include<bits/stdc++.h>
#define Rint register int
using namespace std;
typedef long long LL;
const int N = 100003;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0; bool f = false;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
int n, f[2][N]; LL ans;
int main(){
    read(n);
    for(Rint i = 1, x, y, xx, yy;i <= (n<<1);++ i){
        read(x); read(y); -- y; xx = min(n, max(1, x)); yy = y > 0;
        ans += abs(xx - x) + abs(yy - y); ++ f[yy][xx];
    }
    for(Rint i = 1, x;i <= n;++ i){
        -- f[0][i]; -- f[1][i];
        if(f[0][i] > 0 && f[1][i] < 0){
            x = min(f[0][i], -f[1][i]);
            f[0][i] -= x; f[1][i] += x; ans += x;
        } else if(f[0][i] < 0 && f[1][i] > 0){
            x = min(-f[0][i], f[1][i]);
            f[0][i] += x; f[1][i] -= x; ans += x;
        }
        f[0][i+1] += f[0][i]; ans += abs(f[0][i]);
        f[1][i+1] += f[1][i]; ans += abs(f[1][i]);
    } printf("%lld\n", ans);
}

LOJ2759「JOI 2014 Final」飛天鼠

題目描述:給定 \(n\) 棵樹,第 \(i\) 棵樹的高度是 \(h_i\)。你初始在第 \(1\) 棵樹上,距離地面 \(X\) 米。你可以在 \(m\) 對樹之間飛行,若你當前距離地面 \(h\) 米,飛行時間為 \(t\) 秒,則飛過去之後距離地面 \(h-t\) 米,若 \(h-t<0\)\(h-t\) 大於樹高則無法飛行。你也可以花費 \(1\text{s}\) 向上/下 \(1\) 米。求到達第 \(n\) 棵樹的頂端的最小花費時間。

資料範圍:\(n\le 10^5,m\le 3\times 10^5,h_i\le 10^9,0\le X\le h_1\)

有一個結論,由於上升和下降的代價是一樣的,所以若當前飛行的高度合法,則不會上升/下降,否則會導致之後的決策更劣。

於是到達每個點的高度是不需決策的,可以求出來。直接 dij 即可,時間複雜度 \(O(m\log m)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define se second
using namespace std;
typedef long long LL;
typedef pair<LL, int> pii;
const int N = 100003, M = N*6;
const LL inf = 0x3f3f3f3f3f3f3f3fll;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0; bool f = false;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, m, h[N], he[N], head[N], to[M], nxt[M], w[M]; LL dis[N];
void add(int a, int b, int c){
    static int cnt = 0;
    to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt; w[cnt] = c;
}
priority_queue<pii, vector<pii>, greater<pii> > pq;
bool vis[N];
int main(){
    read(n); read(m); read(he[1]);
    for(Rint i = 1;i <= n;++ i) read(h[i]);
    for(Rint i = 1, a, b, c;i <= m;++ i){
        read(a); read(b); read(c);
        if(c <= h[a]) add(a, b, c);
        if(c <= h[b]) add(b, a, c);
    }
    memset(dis, 0x3f, sizeof dis); pq.push(MP(dis[1] = 0, 1));
    while(!pq.empty()){
        int x = pq.top().se; pq.pop(); if(vis[x]) continue; vis[x] = true;
        for(Rint i = head[x];i;i = nxt[i]){
            int tmp = max(min(he[x] - w[i], h[to[i]]), 0);
            if(chmin(dis[to[i]], dis[x] + w[i] + abs(tmp + w[i] - he[x]))){
                he[to[i]] = tmp; pq.push(MP(dis[to[i]], to[i]));
            }
        }
    }
    if(dis[n] >= inf) puts("-1");
    else printf("%lld\n", dis[n] + h[n] - he[n]);
}

LOJ3282「JOISC 2020 Day4」治療計劃

題目連結:LOJ

若有兩個治療方案 \((L_i,R_i,T_i)\)\((L_j,R_j,T_j)\),則能合併為一個區間當且僅當 \(R_i-L_j+1\ge |T_i-T_j|\)。目標就是合併出 \([1,n]\)。考慮建圖,原點向所有 \(L_i=1\)\(i\) 連邊,權值 \(c_i\),匯點同理。若 \(i\) 能與 \(j\) 合併則

將不等式中的絕對值拆開:

  1. \(T_i\le T_j\),則 \(R_i+T_i+1\ge T_j+L_j\)
  2. \(T_i>T_j\),則 \(R_i-T_i+1\ge L_j-T_j\)

將所有治療方案按照 \(T_i\) 排序。使用兩棵線段樹維護 \(L_j\pm T_j\),由於每個點的所有入邊權值相同,所以每個點只會被更新一次。時間複雜度 \(O(n\log n)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define se second
using namespace std;
typedef long long LL;
typedef pair<LL, int> pii;
const int N = 100003;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0; bool f = false;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
priority_queue<pii, vector<pii>, greater<pii> > pq;
int n, m; LL dis[N]; vector<int> E;
struct Node {
    int t, l, r, c;
    bool operator < (const Node &o) const {return t < o.t;}
} a[N];
struct SegTree {
    int minv[N<<2];
    void pushup(int x){minv[x] = min(minv[x<<1], minv[x<<1|1]);}
    void build(int x, int L, int R, int w){
        if(L == R){minv[x] = a[L].l + w * a[L].t; return;}
        int mid = L + R >> 1;
        build(x<<1, L, mid, w); build(x<<1|1, mid+1, R, w);
        pushup(x);
    }
    void qry(int x, int L, int R, int l, int r, int val){
        if(minv[x] > val) return;
        if(L == R){E.push_back(L); return;}
        int mid = L + R >> 1;
        if(l <= mid) qry(x<<1, L, mid, l, r, val);
        if(mid < r) qry(x<<1|1, mid+1, R, l, r, val);
    }
    void mdf(int x, int L, int R, int p){
        if(L == R){minv[x] = 2e9; return;}
        int mid = L + R >> 1;
        if(p <= mid) mdf(x<<1, L, mid, p);
        else mdf(x<<1|1, mid+1, R, p);
        pushup(x);
    }
} t[2];
int main(){
    read(n); read(m);
    for(Rint i = 1;i <= m;++ i){read(a[i].t); read(a[i].l); read(a[i].r); read(a[i].c);}
    sort(a + 1, a + m + 1);
    memset(dis, 0x3f, sizeof dis);
    t[0].build(1, 1, m, -1); t[1].build(1, 1, m, 1);
    for(Rint i = 1;i <= m;++ i)
        if(a[i].l == 1){
            pq.push(MP(dis[i] = a[i].c, i));
            t[0].mdf(1, 1, m, i); t[1].mdf(1, 1, m, i);
        }
    while(!pq.empty()){
        int x = pq.top().se; pq.pop(); E.clear();
        if(a[x].r == n){printf("%lld\n", dis[x]); return 0;}
        if(x > 1) t[0].qry(1, 1, m, 1, x - 1, a[x].r - a[x].t + 1);
        if(x < m) t[1].qry(1, 1, m, x + 1, m, a[x].r + a[x].t + 1);
        for(Rint v : E){
            pq.push(MP(dis[v] = dis[x] + a[v].c, v));
            t[0].mdf(1, 1, m, v); t[1].mdf(1, 1, m, v);
        }
    } puts("-1");
}

LOJ3012「JOI 2019 Final」有趣的家庭菜園 3

題目描述:給定長為 \(n\) 的字串 \(S\),求最少的相鄰交換次數,使得相鄰兩個字元不等。

資料範圍:\(n\le 400,|\Sigma|=3\)

ntf說這事sb?

求相鄰交換次數,相當於求逆序對個數,即原來與現在順序不同的數對個數。直接 dp...?

時間複雜度 \(O(n^3)\)

CF Gym 102059 D Dumae

題目描述:給定 \(n\) 個區間 \([l_i,r_i]\)\(m\) 個二元組 \((u_i,v_i)\),構造長為 \(n\) 的排列 \(p\) 使得 \(p_i\in[l_i,r_i]\)\(p_{u_i}<p_{v_i}\)。需判斷無解。

資料範圍:\(n\le 3\times 10^5,m\le 10^6,1\le l_i\le r_i\le n,1\le u_i,v_i\le n\)

CF Gym 101741 H Compressed Spanning Subtrees

這事一道互動題

題目描述:互動器有一棵 \(n\) 個點的樹,你至多詢問 \(2550\) 次點集 \(X\),互動器會告訴你 \(X\) 的虛樹大小。求這棵樹。

資料範圍:\(n\le 100\)

CF Gym 102129 C Medium Hadron Collider

這是一道互動題

\(p=2\times 10^7-1\)。互動器有一個自然數 \(a\le 10^7\),設 \(F(x)=-(x+1)^a(x-1)^{10^7-1-a}\),你可以至多詢問 \(10\) 次正整數 \(k\in[2^7,2^9)\),互動器會告訴你 \([x^k]F(x)\bmod p\)。對於所有自然數 \(k<2^7\),求 \([x^k]F(x)\bmod p\)

\(F(x)=(x+1)^a(x-1)^b\),則 \(F'(x)(x^2-1)=F(x)((N-1)x+N-1-2a)\)

\((N-n)f_{n-1}+(n+1)f_{n+1}=(2a+1-N)f_n\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 1e7, mod = 19999999;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0; bool f = false;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
void qmo(int &x){x += x >> 31 & mod;}
int ksm(int a, int b){int res = 1; for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod; return res;}
int f0, f1, f2, n, fac[N], inv[N];
int query(int x){printf("? %d\n", x+1); fflush(stdout); read(x); qmo(x = -x); return x;}
int C(int n, int m){if(m < 0 || n < m) return 0; return (LL) fac[n] * inv[m] % mod * inv[n-m] % mod;}
int main(){
    read(n); f0 = query(128); f1 = query(n = 129); f2 = query(130);
    for(;!f1;++ n){f0 = f1; f1 = f2; f2 = query(n+2);}
    int a = (((LL) (N-n) * f0 + (n+1ll) * f2) % mod * ksm(f1, mod-2) % mod + N - 1) % mod;
    if(a & 1) a += mod; a >>= 1; putchar('!'); fac[0] = 1;
    for(Rint i = 1;i < N;++ i) fac[i] = (LL) fac[i-1] * i % mod;
    inv[N-1] = ksm(fac[N-1], mod-2);
    for(Rint i = N-1;i;-- i) inv[i-1] = (LL) inv[i] * i % mod;
    for(Rint i = 0;i < 128;++ i){
        int res = 0;
        for(Rint j = 0;j <= i;++ j){
            int tmp = (LL) C(N-1-a, j) * C(a, i-j) % mod;
            if(a-j & 1) qmo(res -= tmp); else qmo(res += tmp - mod);
        }
        if(res >= N) res -= mod; printf(" %d", res);
    }
}

-LOJ2345「JOI 2016 Final」領地

題目描述:給定一個長為 \(n\) 的字串 \(S\) 和正整數 \(k\),初始在 \((0,0)\),每次按照 \(S\) 中的字元移動,重複 \(k\) 次,求有多少個 \(1\times 1\) 正方形的四角都被經過。

資料範圍:\(n\le 10^5,k\le 10^9\)

-CF1406E Deleting Numbers

這是一道互動題

題目描述:給定正整數 \(n\),互動器有一個正整數 \(x\le n\),初始有一個集合 \(S=[1,n]\cap\Z\),你可以詢問至多 \(9999\) 次正整數 \(a\le n\) (1) \(S\)\(a\) 的倍數的個數,(2) \(S\)\(a\) 的倍數的個數,並將這些數中除了 \(x\) 之外的數刪掉(要求 \(a\ge 2\))。求 \(x\)

資料範圍:\(n\le 10^5\)

CF Gym 102354 Square Root Partitioningsss

題目描述:給定 \(n\) 個正整數 \(a_i\),求有多少個 \(\sqrt{a_1}\pm\sqrt{a_2}\pm\dots\pm\sqrt{a_n}=0\)

資料範圍:\(n\le 36,a_i\le 10^{10^5}\)

\(b_i\) 表示 \(a_i\) 的最大非平方因子,則對於 \(b_i\) 不同的 \(a_i\) 之間互不影響,將 \(=0\) 的方案數相乘即可。

如何判斷 \(b_i\) 相同,可以取 \(B\)\([10^{14},10^{14}+10^7]\) 的質數(這些質數沒有整除任何一個 \(a_i\) 的),判斷 \(a_i\) 的二次剩餘存在性,假定存在性相同的數的 \(b_i\) 相同,錯誤概率 \(O(n^22^{-B})\)

至於計算答案,有 \(1-\frac{1}{2^B-1}\) 的概率找出一個質數 \(p\),滿足這些數在 \(\text{mod} \ p\) 意義下都有二次剩餘。在 \(\text{mod} \ p\) 意義下直接開根,用 meet-in-the-middle 計算即可。時間複雜度 \(O((n+B)\log a_i+n2^{\frac{n}{2}})\)

寫程式碼的時候突然忘記Cipolla了,想到如果取出的質數 \(p\) 都是模 \(4\)\(3\) 的,就可以直接用 \(a^{\frac{p+1}{4}}\) 計算 \(\sqrt a\)

這就引出了 \(\color{red}{\text{Fulisike}}\)極簡做法,設 \(p=10^{17}+3\),直接預設 \(\sqrt x=x^{\frac{p+1}{4}}\bmod p\) 來計算,因為若包含不同 \(b_i\) 的數,算出 \(0\) 的概率極小(\(O(\frac{2^n}{p})\)),但是可以被定向卡,可原題是 ACM...

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define PB push_back
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef long double LD;
typedef pair<int, int> pii;
const int N = 100003, K = 1e7 + 1, B = 30;
const LL V = 1e14;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0; bool f = false;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
LL mul(LL a, LL b, LL mod){LL res = a * b - (LL)((LD) a / mod * b + 0.5) * mod; return (res % mod + mod) % mod;}
LL ksm(LL a, LL b, LL mod){
    LL res = 1;
    for(;b;b >>= 1, a = mul(a, a, mod)) if(b & 1) res = mul(res, a, mod);
    return res;
}
int n, tot, now[36], cnt; bool notp[K], np[K], vis[36];
LL sq[36], r[30][36], rem[36], pri[30], ans = 1;
char s[36][N]; vector<LL> pis; map<LL, int> S;
LL calc(int id, LL p){
    LL res = 0;
    for(const char *c = s[id];*c;++ c) res = (res * 10 + *c - '0') % p;
    return res;
}
int main(){
    srand(time(0)); notp[0] = notp[1] = true;
    for(Rint i = 2;i < K;++ i) if(!notp[i]){
        for(Rint j = i<<1;j < K;j += i) notp[j] = true;
        for(LL j = ((V-1)/i+1)*i;j < V+K;j += i) np[j-V] = true;
    }
    for(Rint i = 0;i < K;++ i) if(!np[i] && (i&3)==3) pis.PB(i+V);
    random_shuffle(pis.begin(), pis.end()); read(n);
    for(Rint i = 0;i < n;++ i) scanf("%s", s[i]);
    for(Rint i = 0, u = 0;i < B;++ u){
        bool f = true;
        for(Rint j = 0;f && j < n;++ j)
            if(!(r[i][j] = calc(j, pis[u]))) f = false;
        if(f) pri[i++] = pis[u];
    }
    for(Rint i = 0;i < n;++ i)
        for(Rint j = 0;j < B;++ j) if(ksm(r[j][i], pri[j]-1>>1, pri[j]) == 1) rem[i] |= 1 << j;
    for(Rint i = 0;i < n;++ i) if(!vis[i]){
        cnt = 0;
        for(Rint j = i;j < n;++ j) if(rem[i] == rem[j]){now[cnt++] = j; vis[j] = true;}
        int p = -1; S.clear();
        for(Rint j = 0;j < B;++ j) if(rem[i] >> j & 1){p = j; break;}
        for(Rint j = 0;j < cnt;++ j) sq[j] = ksm(r[p][now[j]], pri[p]+1>>2, pri[p]);
        int k = cnt>>1; LL res = 0;
        for(Rint i = 0;i < (1<<k);++ i){
            LL sum = 0;
            for(Rint j = 0;j < k;++ j)
                if(i>>j&1){sum += sq[j]; if(sum >= pri[p]) sum -= pri[p];}
                else {sum -= sq[j]; if(sum < 0) sum += pri[p];}
            ++ S[sum];
        }
        for(Rint i = 0;i < (1<<cnt-k);++ i){
            LL sum = 0;
            for(Rint j = 0;j < cnt-k;++ j)
                if(i>>j&1){sum += sq[j+k]; if(sum >= pri[p]) sum -= pri[p];}
                else {sum -= sq[j+k]; if(sum < 0) sum += pri[p];}
            res += S[sum];
        }
        ans *= res;
    }
    printf("%lld\n", ans>>1);
}

CF418E Tricky Password

題目描述:給定 \(n\) 列的矩陣 \(a\),第一行給出,\(a_{i,j}\)\(a_{i-1,j}\)\(a_{i-1,1},\dots,a_{i-1,j}\) 中的出現次數(\(i>1\))。\(q\) 次操作 (1) 第一行單點修改 (2) 單點查詢。

資料範圍:\(n,q\le 10^5\)

可以證明 \(a\) 從第二行開始週期為 \(2\)。因為從第二行開始,對於每個字首,都有 \(i\) 的出現次數不小於 \(i-1\) 的出現次數。根據這個性質可得,兩行之後變為它自己。

第一行顯然,第二行可以用平衡樹維護出現次數我偷懶寫了個動態開點線段樹,第三行 \(a_{3,p}\)\(a_{1,[1,p]}\) 中出現次數不小於 \(a_{2,p}\) 的數的種數,使用莫隊維護桶和桶的桶即可。空間複雜度 \(O(n)\)\(O(n\log n)\),時間複雜度 \(O(n\sqrt n)\)

#include<bits/stdc++.h>
#define Rint register int
#define PB push_back
using namespace std;
typedef pair<int, int> pii;
const int N = 200003, M = N<<5;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0; bool f = false;
    for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    if(f) x = -x;
}
int n, m, B, a[N], opt[N], x[N], y[N], ans[N], tot, ctt, val[N], len;
struct Qry {
    int p, x, id;
    Qry(int _p = 0, int _x = 0, int _id = 0): p(_p), x(_x), id(_id){}
    inline bool operator < (const Qry &o) const {
        if(p / B != o.p / B) return p / B < o.p / B;
        if(p / B & 1) return id > o.id; return id < o.id;
    }
} q[N], c[N];
int rt[N], ls[M], rs[M], sum[M], nc;
void change(int &x, int L, int R, int p, int v){
    if(!x) x = ++ nc; sum[x] += v; if(L == R) return;
    int mid = L + R >> 1;
    if(p <= mid) change(ls[x], L, mid, p, v);
    else change(rs[x], mid+1, R, p, v);
}
int query(int x, int L, int R, int r){
    if(!x) return 0; if(R <= r) return sum[x];
    int mid = L + R >> 1, res = query(ls[x], L, mid, r);
    if(mid < r) res += query(rs[x], mid+1, R, r);
    return res;
}
int qr, qt, cnt[N], ccnt[N]; unordered_set<int> S;
void add(int x){-- ccnt[cnt[x]]; if(cnt[x] == B) S.insert(x); ++ cnt[x]; ++ ccnt[cnt[x]];}
void del(int x){-- ccnt[cnt[x]]; -- cnt[x]; ++ ccnt[cnt[x]]; if(cnt[x] == B) S.erase(x);}
int main(){
    read(n); B = sqrt(n) + 1; len = n;
    for(Rint i = 1;i <= n;++ i){read(a[i]); val[i-1] = a[i];} read(m);
    for(Rint i = 1;i <= m;++ i){
        read(opt[i]); read(x[i]); read(y[i]);
        if(opt[i] == 1) val[len++] = x[i];
    }
    sort(val, val + len); len = unique(val, val + len) - val;
    for(Rint i = 1;i <= n;++ i){
        a[i] = lower_bound(val, val + len, a[i]) - val;
        change(rt[a[i]], 1, n, i, 1);
    }
    for(Rint i = 1;i <= m;++ i)
        if(opt[i] == 1){
            x[i] = lower_bound(val, val + len, x[i]) - val;
            c[++ctt] = Qry(y[i], x[i], i);
            change(rt[a[y[i]]], 1, n, y[i], -1);
            change(rt[x[i]], 1, n, y[i], 1); swap(a[y[i]], x[i]);
        } else {
            if(x[i] == 1){ans[i] = val[a[y[i]]]; continue;}
            if(x[i] & 1){
                q[++tot] = Qry(y[i], query(rt[a[y[i]]], 1, n, y[i]), i);
            }else ans[i] = query(rt[a[y[i]]], 1, n, y[i]);
        }
    sort(q + 1, q + tot + 1);
    for(Rint i = m;i;-- i) if(opt[i] == 1) swap(a[y[i]], x[i]);
    for(Rint i = 1;i <= tot;++ i){
        while(qr < q[i].p) add(a[++ qr]);
        while(qr > q[i].p) del(a[qr --]);
        while(qt < ctt && c[qt+1].id <= q[i].id){++ qt; if(c[qt].p <= qr){del(a[c[qt].p]); add(c[qt].x);} swap(a[c[qt].p], c[qt].x);}
        while(qt && c[qt].id > q[i].id){if(c[qt].p <= qr){del(a[c[qt].p]); add(c[qt].x);} swap(a[c[qt].p], c[qt].x); -- qt;}
        int &ret = ans[q[i].id];
        if(q[i].x > B){for(Rint u : S) ret += cnt[u] >= q[i].x;}
        else {ret = S.size(); for(Rint j = q[i].x;j <= B;++ j) ret += ccnt[j];}
    }
    for(Rint i = 1;i <= m;++ i) if(opt[i] == 2) printf("%d\n", ans[i]);
}

CF804E The same permutation

題目描述:給定正整數 \(n\),求出 \((i,j)\)\(1\le i<j\le n\),總共 \(\frac{n(n-1)}{2}\) 個)的一個排列,使得對於任意一個長為 \(n\) 的排列 \(p\),按照這個順序交換 \((p_i,p_j)\),最後得到它自己。需判斷無解。

資料範圍:\(n\le 1000\)

\(2\not|\frac{n(n-1)}2\)\(n\bmod 4\ge 2\),則逆序對數奇偶性不對,必定無解。

考慮手玩一下,\(n=4\) 時可以 \((1,2),(3,4),(1,4),(2,3),(1,3),(2,4)\)\(n=5\) 時可以 \(\dots,(1,5),(2,5),(1,3),(2,4),(4,5),(3,5)\)

\(n\) 個元素分為 \(\lfloor\frac{n}{4}\rfloor\) 組,每組 \(4\) 個元素。若多一個元素則把它加入每一組。考慮 \((1,2,3,4)\Rightarrow (2,1,4,3)\) 花費四步操作,則可以解決兩組之間的 \(16\) 步。

#include<bits/stdc++.h>
#define Rint register int
using namespace std;
int n, t[10];
void work0(){
    printf("%d %d\n", t[0], t[1]);
    printf("%d %d\n", t[2], t[3]);
    printf("%d %d\n", t[0], t[3]);
    printf("%d %d\n", t[1], t[2]);
    printf("%d %d\n", t[0], t[2]);
    printf("%d %d\n", t[1], t[3]);
}
void work1(){
    printf("%d %d\n", t[0], t[1]);
    printf("%d %d\n", t[2], t[3]);
    printf("%d %d\n", t[0], t[3]);
    printf("%d %d\n", t[1], t[2]);
    printf("%d %d\n", t[0], t[4]);
    printf("%d %d\n", t[1], t[4]);
    printf("%d %d\n", t[0], t[2]);
    printf("%d %d\n", t[1], t[3]);
    printf("%d %d\n", t[3], t[4]);
    printf("%d %d\n", t[2], t[4]);
}
void work2(){
    printf("%d %d\n", t[0], t[4]);
    printf("%d %d\n", t[1], t[5]);
    printf("%d %d\n", t[0], t[5]);
    printf("%d %d\n", t[1], t[4]);
    printf("%d %d\n", t[0], t[6]);
    printf("%d %d\n", t[1], t[7]);
    printf("%d %d\n", t[0], t[7]);
    printf("%d %d\n", t[1], t[6]);
    printf("%d %d\n", t[2], t[4]);
    printf("%d %d\n", t[3], t[5]);
    printf("%d %d\n", t[2], t[5]);
    printf("%d %d\n", t[3], t[4]);
    printf("%d %d\n", t[2], t[6]);
    printf("%d %d\n", t[3], t[7]);
    printf("%d %d\n", t[2], t[7]);
    printf("%d %d\n", t[3], t[6]);
}
int main(){
    scanf("%d", &n);
    if((n & 3) > 1){puts("NO"); return 0;} puts("YES");
    for(Rint i = 1;i < n;i += 4){
        for(Rint j = 0;j < 4;++ j) t[j] = i+j;
        if(n & 1){t[4] = n; work1();} else work0();
        for(Rint j = i + 4;j < n;j += 4){
            for(Rint k = 0;k < 4;++ k){t[k] = i+k; t[k+4] = j+k;}
            work2();
        }
    }
}

CF Gym 102443K RotationAlmostSort

題目描述:給定正整數 \(n\),對於所有 \(n\times n\) 的矩陣,你需要輸出一個程式,包含不超過 \(10^5\) 條語句。語句形如 \(A>B?C\),其中 \(A,B\) 為兩個格子,\(C\) 是一個 \(2\times 2\) 的子矩陣,表示若 \(A\) 中的數大於 \(B\) 中的數,則將 \(C\) 中的數逆時針旋轉。你需要將後 \(n-2\) 行按從上到下,從左往右的順序排序。

資料範圍:\(3\le n\le 9\)

發現可以將 \(2\times 2\) 的矩陣取出最大值放到某一個位置(見程式)

於是氣泡排序即可。

#include<bits/stdc++.h>
#define Rint register int
using namespace std;
const int dx[] = {0, 1, 1, 0}, dy[] = {0, 0, 1, 1};
int n;
void out(int x1, int y1, int x2, int y2, int x3, int y3){printf("%c%d > %c%d ? %c%d\n", y1+'a'-1, x1, y2+'a'-1, x2, y3+'a'-1, x3);}
void work(int x, int y, int c){for(Rint i = c+1;i < c+4;++ i) out(x+dx[i&3], y+dy[i&3], x+dx[c], y+dy[c], x, y);}
int main(){
    scanf("%d", &n);
    for(Rint i = n;i > 2;-- i){
        for(Rint j = n;j;-- j){
            for(Rint x = 1;x <= i-2;++ x)
                for(Rint y = n-1;y;-- y) work(x, y, 1);
            for(Rint x = 1;x <= j-1;++ x) work(i-1, x, 2);
        }
        out(i, 2, i, 1, i-1, 1); out(i-1, 2, i, 2, i-1, 1); out(i-1, 1, i-1, 2, i-1, 1);
        work(i-2, 1, 1); out(i, 1, i-1, 1, i-1, 1);
    }
}

Moscow Pre-Finals Workshop 2019. KAIST Contest

A A Plus Equals B

題目描述:給定兩個正整數 \(a,b\),你可以進行不超過 \(5000\) 次操作,形如 (a|b)+=(a|b),使得 \(a=b\)

資料範圍:\(a,b\le 10^{18}\)

容易發現若 \(a,b\) 中至少有一個偶數,則可以 1 步操作直接將其 /2,否則不妨設 \(a<b\),則執行操作 b+=a,a+=a,可以變為 \((a,\frac{a+b}2)\),此時 \(|a-b|\) 減半,至多做 \(\log_2|a-b|\) 次操作變為前者的情況。

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const char *str[] = {"A+=A", "B+=B", "A+=B", "B+=A"};
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
LL a, b; vector<int> ans;
int main(){
	read(a); read(b);
	while(a ^ b){
		while(!(a & 1) && !(b & 1)){a >>= 1; b >>= 1;}
		while(!(a & 1)){a >>= 1; ans.PB(1);}
		while(!(b & 1)){b >>= 1; ans.PB(0);}
		if(a < b){ans.PB(3); ans.PB(0); b = a + b >> 1;}
		else if(a > b){ans.PB(2); ans.PB(1); a = a + b >> 1;}
	}
	printf("%d\n", ans.size());
	for(Rint u : ans) puts(str[u]);
}

F Fruit Tree

題目描述:【總統選舉】搬到了樹上iee

I Increasing Sequence

題目描述:給定長為 \(n\) 的排列 \(p_i\),對於所有 \(i\in[1,n]\cap\Z\),求有多少個 \(j\ne i\) 使得刪去 \(p_j\) 之後經過 \(p_i\) 的最長上升子序列長度降低。

資料範圍:\(n\le 2.5\times 10^5\)

看上去就像我不會的套路題

容易發現 \(j<i\)\(j>i\) 的情況互相獨立,於是可以分開做。僅考慮 \(j<i\) 的情況,另一種同理。

\(pre_i\) 表示 \(j_\max\),則以 \(pre_i\)\(i\) 的父親建樹,\(dep_i-1\) 就是答案。

如何求 \(pre_i\) 呢,設 \(dp_i\) 表示以 \(i\) 結尾的最長上升子序列長度,則 \(pre_i=\text{LCA}\{j|j<i,a_j<a_i,dp_j+1=dp_i\}\),由於相同 \(dp_i\)\(i\) 滿足 \(p_i\) 遞減,於是由歸納法得,設 \(l_i,r_i\) 為這個集合的最值,則 \(pre_i=\text{LCA}(l_i,r_i)\)

這裡需要邊求 \(\text{LCA}\) 邊加葉子,只能使用倍增法。時空複雜度 \(O(n\log n)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 250003;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
int n, a[N], ans[2][N], val[N], len, p[18][N], dep[N];
vector<int> vec[N];
int lca(int u, int v){
	if(dep[u] < dep[v]) swap(u, v);
	for(Rint i = 17;~i;-- i) if(dep[u] - dep[v] >= (1 << i)) u = p[i][u];
	if(u == v) return u;
	for(Rint i = 17;~i;-- i) if(dep[u] >= (1 << i) && p[i][u] != p[i][v]){u = p[i][u]; v = p[i][v];}
	return p[0][u];
}
void solve(int *ans){
	for(Rint i = 0;i < n;++ i) vec[i].clear(); dep[n] = len = 0;
	for(Rint i = 0;i < n;++ i){
		int p = lower_bound(val, val + len, a[i]) - val;
		val[p] = a[i]; vec[p].PB(i); if(p == len) ++ len;
	}
	for(Rint u : vec[0]){p[0][u] = n; dep[u] = 1;}
	for(Rint i = 1;i < len;++ i){
		int l = 0, r = 0, tmp = vec[i-1].size();
		for(Rint u : vec[i]){
			while(a[vec[i-1][l]] > a[u]) ++ l;
			while(r < tmp && vec[i-1][r] < u) ++ r;
			dep[u] = dep[p[0][u] = lca(vec[i-1][l], vec[i-1][r-1])] + 1;
			for(Rint j = 1;(1<<j) <= dep[u];++ j) p[j][u] = p[j-1][p[j-1][u]];
		}
	}
	for(Rint i = 0;i < n;++ i) ans[i] = dep[i] - 1;
}
int main(){
	read(n);
	for(Rint i = 0;i < n;++ i){read(a[i]); --a[i];}
	solve(ans[0]); reverse(a, a + n);
	for(Rint i = 0;i < n;++ i) a[i] = n - a[i] - 1;
	solve(ans[1]);
	for(Rint i = 0;i < n;++ i) printf("%d ", ans[0][i] + ans[1][n-i-1]);
}

C Cactus Determinant

題目描述:給定 \(n\) 個點 \(m\) 條邊的邊-仙人掌,求鄰接矩陣行列式\(\bmod 993244853\)

資料範圍:\(n\le 5\times 10^4\)

關於行列式的符號,實際上就是 \(n\) 減去 \(i\rightarrow p_i\) 所形成的環個數。

列舉這個圖的子圖,滿足每個連通塊是一個匹配或一個簡單環,貢獻就是 \(2^{環個數}(-1)^{n-總數}\)

直接建出圓方樹然後做樹形 dp 即可雖然我是第一次寫。時空複雜度 \(O(n+m)\)

關於圓點和方點貢獻的問題,實際上圓方樹等價於將環從一個點(父親)上劈開,總體貢獻要記到父親上去,要稍微改一改轉移方程。

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 100003, mod = 993244853;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int n, m, head[N], to[N<<1], nxt[N<<1], dp[N][2], dep[N], ctot, fa[N]; bool vis[N];
vector<int> E[N], cyc; 
void add(int a, int b){
	static int cnt = 0;
	to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt;
}
void dfs(int x){
	vis[x] = fa[x];
	for(Rint i = head[x];i;i = nxt[i]) if(to[i] != fa[x]){
		if(!dep[to[i]]){dep[to[i]] = dep[x] + 1; fa[to[i]] = x; dfs(to[i]);}
		else if(dep[to[i]] < dep[x]){
			++ ctot; int tmp = x;
			while(tmp != to[i]){
				E[tmp].PB(ctot); E[ctot].PB(tmp);
				vis[tmp] = false; tmp = fa[tmp];
			}
			E[tmp].PB(ctot); E[ctot].PB(tmp);
		}
	} if(vis[x]){E[x].PB(fa[x]); E[fa[x]].PB(x);}
}
void dfs2(int x, int f = 0){
	for(Rint v : E[x]) if(v != f) dfs2(v, x);
	if(x <= n){
		dp[x][0] = 1;
		for(Rint v : E[x]) if(v != f){
			dp[x][1] = ((LL) dp[x][1] * dp[v][1] + (LL) dp[x][0] * dp[v][0]) % mod;
			dp[x][0] = (LL) dp[x][0] * dp[v][1] % mod;
		}
		if(dp[x][1]) dp[x][1] = mod - dp[x][1];
	} else {
		int pos = find(E[x].begin(), E[x].end(), f) - E[x].begin(); cyc.clear();
		for(Rint i = pos+1;i < E[x].size();++ i) cyc.PB(E[x][i]);
		for(Rint i = 0;i < pos;++ i) cyc.PB(E[x][i]);
		int len = cyc.size(), t[2][2] = {{0, 1}, {0, 0}};
		for(Rint v : cyc){
			t[1][0] = (LL) t[0][1] * dp[v][0] % mod;
			t[1][1] = ((LL) t[0][1] * dp[v][1] + (LL) (mod - t[0][0]) * dp[v][0]) % mod;
			t[0][0] = t[1][0]; t[0][1] = t[1][1];
		}
		dp[x][0] = t[0][0]; dp[x][1] = t[0][1];
		t[0][0] = 0; t[0][1] = dp[cyc[0]][0];
		for(Rint i = 1;i < len;++ i){
			t[1][0] = (LL) t[0][1] * dp[cyc[i]][0] % mod;
			t[1][1] = ((LL) t[0][1] * dp[cyc[i]][1] + (LL) (mod - t[0][0]) * dp[cyc[i]][0]) % mod;
			t[0][0] = t[1][0]; t[0][1] = t[1][1];
		}
		qmo(dp[x][0] += t[0][1] - mod); int tmp = (len & 1) ? 2 : mod-2;
		for(Rint v : cyc) tmp = (LL) tmp * dp[v][0] % mod;
		qmo(dp[x][0] += tmp - mod);
	}
}
int main(){
	read(n); read(m);
	for(Rint i = 1, u, v;i <= m;++ i){
		read(u); read(v);
		add(u, v); add(v, u);
	}
	ctot = n; dep[1] = 1; dfs(1); dfs2(1); printf("%d\n", dp[1][1]);
}

H Hard to Explain

題目描述:給定一棵以 \(1\) 為根的有 \(n\) 個點的樹,每個點上有三個正整數權值 \(a_i,b_i,c_i\),有 \(q\) 次詢問,每次詢問給出點 \(v\) 和自然數 \(t\),求 \(1\)\(v\) 的路徑上,所有 \(c_i\ge t\) 的節點中 \(b_it+a_i\) 的最小值。

資料範圍:\(n\le 8\times 10^4,q\le 1.6\times 10^5,a_i,b_i,c_i,t\le 10^9,c_1=10^9,b_{fa_x}\le b_x(x\ne 1),v\le n\)

直接上可持久化李超樹,然後複雜度 \(O((n+q)\log n)\) 就做完了?斜率的單調性有用麼...

G Good Set

題目描述:給定正整數 \(k\),定義全集 \(U=[0,2^k)\cap\N\),給定集合 \(S\subseteq U\),求有多少個集合 \(A\) 滿足 \(S\subseteq A\subseteq U\),且 \(A\) 對於 \(|\)\(\&\) 運算封閉。

資料範圍:\(k\le 7\)

定義 \(U\) 的子集族 \(\mathcal{F}\) 表示所有對於 \(|\)\(\&\) 封閉的,包含 \(0\)\(2^k-1\) 的子集組成的集合。

定義有向圖 \(G=(V,E)\) 滿足傳遞性當且僅當 \((u,v),(v,w)\in E\Rightarrow (u,w)\in E\)

下面證明:\(\mathcal F\)\(|V|=k\) 的滿足傳遞性的有向圖可以一一對應。

對於一個子集 \(T\in\mathcal F\),定義 \(f(T)=(V,E)\),其中 \(|V|=k\),且 \((u,v)\in E\Leftrightarrow(\forall A\in T,u\in A\Rightarrow v\in A)\)。顯然 \(f(T)\) 具有傳遞性。

對於 \(k\) 個點的滿足傳遞性的有向圖 \(G=(V,E)\),定義 \(g(G)\subseteq U\),其中 \(A\in g(G)\Leftrightarrow(\forall (u,v)\in E,u\in A\Rightarrow v\in A)\)。易得 \(g(G)\in\mathcal F\)

不易得感性理解一下 \(f(g(G))=G,g(f(T))=T\),於是就證明了 \(f,g\) 互為逆對映,證畢。

問題就可以轉化為其對應的有向圖數量。先列舉 \(A\) 中的最小集 \(S\) 和最大集 \(T\),顯然 \(\forall X\in A,S\subseteq X\subseteq T\),只需考慮 \(T-S\) 的部分即可。dfs 列舉邊並通過 bitmask 判斷即可,可列舉的順序非常迷,不知為何這樣是最快的。

時間複雜度據說是 \(O(k\cdot Ans)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 128;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
int k, n, x[N], r[7], m, minv, maxv, ans, cnt, uy[7], vy[7], un[7], vn[7];
bool e[7][7]; pii a[N];
void dfs(int d){
	if(d == cnt){++ ans; return;}
	int x = a[d].fi, y = a[d].se;
	if(!(uy[x] & vy[y])){
		un[x] |= 1 << y; vn[y] |= 1 << x; dfs(d + 1);
		un[x] ^= 1 << y; vn[y] ^= 1 << x;
	}
	if(!(un[x] & uy[y]) && !(vy[x] & vn[y]) && e[x][y]){
		uy[x] |= 1 << y; vy[y] |= 1 << x; dfs(d + 1);
		uy[x] ^= 1 << y; vy[y] ^= 1 << x;
	}
}
void solve(int S, int T){
	m = cnt = 0;
	for(Rint i = 0;i < k;++ i) if((S ^ T) >> i & 1) r[m++] = i;
	for(Rint a = 0;a < m;++ a)
		for(Rint b = 0;b < m;++ b) e[a][b] = true;
	for(Rint i = 0;i < n;++ i)
		for(Rint a = 0;a < m;++ a) if(x[i] >> r[a] & 1)
			for(Rint b = 0;b < m;++ b) if(!(x[i] >> r[b] & 1)) e[a][b] = false;
	for(Rint i = 0;i < m;++ i)
		for(Rint j = 0;j < i;++ j){
			a[cnt++] = MP(j, i); a[cnt++] = MP(i, j);
		}
	dfs(0);
}
int main(){
	read(k); read(n); minv = (1 << k) - 1;
	for(Rint i = 0;i < n;++ i){read(x[i]); minv &= x[i]; maxv |= x[i];}
	for(Rint S = 0;S < (1 << k);++ S) if((S | maxv) == S){
		for(Rint T = S;T;T = T - 1 & S) if((T & minv) == T) solve(S, T);
		solve(S, 0);
	} printf("%d\n", ans);
}

E Eat Economically

題目描述:給定 \(2n\) 種套餐 \((l,d)\),分別表示這種套餐在午飯和晚飯時的價格。每天要吃午飯和晚飯,每頓飯吃一種套餐,不能吃同一種套餐兩次。對於所有 \(i\in[1,n]\cap\Z\),求吃 \(i\) 天花的最少錢數。

資料範圍:\(n\le 2.5\times 10^5\)

這東西看上去就挺費用流的實際上就是

\(4\) 個 priority_queue 或 set 維護當前可選的 \(l_i,d_i\) 以及 \(l_i,d_i\) 之間互換的代價,貪心選價值最低的增廣路即可。

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 500003;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
int n, a[N], b[N]; LL ans; set<pii> s[4];
int main(){
	read(n); pii tmp = MP(1e9+1, -1);
	for(Rint i = 0;i < 4;++ i) s[i].insert(tmp);
	for(Rint i = 0;i < (n<<1);++ i){read(a[i]); read(b[i]); s[0].insert(MP(a[i], i)); s[1].insert(MP(b[i], i));}
	for(Rint _ = 0;_ < n;++ _){
		int t1 = s[0].begin()->fi, t2 = s[1].begin()->fi + s[3].begin()->fi, u;
		if(t1 <= t2){
			u = s[0].begin()->se; ans += t1;
			s[0].erase(s[0].begin());
			s[1].erase(MP(b[u], u));
		} else {
			u = s[1].begin()->se; ans += t2;
			s[3].insert(MP(a[u] - b[u], u));
			s[1].erase(s[1].begin());
			s[0].erase(MP(a[u], u));
			u = s[3].begin()->se;
			s[3].erase(s[3].begin());
		} s[2].insert(MP(b[u] - a[u], u));
		t1 = s[1].begin()->fi; t2 = s[0].begin()->fi + s[2].begin()->fi;
		if(t1 <= t2){
			u = s[1].begin()->se; ans += t1;
			s[1].erase(s[1].begin());
			s[0].erase(MP(a[u], u));
		} else {
			u = s[0].begin()->se; ans += t2;
			s[2].insert(MP(b[u] - a[u], u));
			s[0].erase(s[0].begin());
			s[1].erase(MP(b[u], u));
			u = s[2].begin()->se;
			s[2].erase(s[2].begin()); 
		} s[3].insert(MP(a[u] - b[u], u)); printf("%lld\n", ans);
	}
}

-D Dijkstra Is Playing At My House

題目描述:給定 \(n\) 個障礙,每個障礙是一個邊平行於座標軸,頂點是整點的矩形 \((a_i,b_i,c_i,d_i)\),你每次可以向 \(4\) 個方向走 \(1\) 單位長度。求從 \((s_x,s_y)\)\((e_x,e_y)\) 的最短路長度。

資料範圍:\(n\le 2.5\times 10^5\),值域 \([0,10^8]\cap\N\)

-J Jealous Teachers

題目描述:給定 \(n-1\) 名學生和 \(n\) 名老師,每名學生分別準備了 \(n\) 朵花,給定 \(m\) 個二元組 \((s_i,t_i)\) 表示第 \(s_i\) 名學生可以將花給第 \(t_i\) 名老師,求構造使每名老師得到 \(n-1\) 朵花的方案,需判斷無解。

資料範圍:\(2\le n\le 10^5,1\le m\le 2\times 10^5\)

2018-2019 9th BSUIR Open Programming Championship. Semifinal

I Equal Mod Segments

題目描述:給定 \(n\) 個正整數 \(a_i\),求有多少個正整數對 \((L,R)\) 滿足 \(1\le L\le R\le n\)\(a_L\bmod a_{L+1}\bmod\dots\bmod a_R=a_R\bmod a_{R-1}\bmod\dots\bmod a_L\)

資料範圍:\(n\le 10^5,a_i\le 3\times 10^5\)

對於左邊的柿子,固定 \(L\) 之後只有 \(\log V\) 種取值,每種取值對應的是 \(R\) 屬於一段區間。右邊同理。

所以求出這 \(O(n\log V)\) 個區間之後掃描線即可,時間複雜度 \(O(n\log n\log V)\)

-B Varvara and matrix

題目描述:給定矩陣 \(\{a\}_{n\times m}\) 和正整數 \(k,A,B\),定義它的權值為滿足 \(1\le x_1<x_2\le n,1\le y_1<y_2\le m\)\(a_{x_1,y_1}=a_{x_1,y_2}=a_{x_2,y_1}=a_{x_2,y_2}\) 的四元組 \((x_1,x_2,y_1,y_2)\) 的數量,構造將所有 \(0\) 變為 \(A\)\(B\) 的方案,使得它的權值不變。

資料範圍:\(2\le n,m\le 10^3,2\le k\le nm,1\le A,B\le k,A\ne B,0\le a_{i,j}\le k\)。每行每列至多隻有 \(1\)\(0\)

弦圖

被迫來學知識點了...

定義:連線環上兩個不相鄰節點的邊稱為。一個無向圖為弦圖當且僅當任意一個大小 \(>3\) 的環都有至少一條弦。無向圖的色數是給點染色的最少顏色數量,使得相鄰的點不同色。無向圖的團數是最大團的大小。無向圖的最大獨立集是滿足其中任意兩個點不相鄰的最大點集。無向圖的最小團覆蓋是用最少的團覆蓋所有點的方案。無向圖中的點 \(v\)單純點當且僅當與其相鄰的點集的誘導子圖是一個團。無向圖的點集的排列 \(v_1,v_2,\dots,v_n\) 是一個完美消除序列當且僅當 \(v_i\)\(\{v_i,v_{i+1},\dots,v_n\}\) 的誘導子圖中是單純點。

結論:團數\(\le\)色數,|最大獨立集|\(\le\)|最小團覆蓋|。弦圖的誘導子圖也是弦圖。

引理:弦圖有單純點。不是完全圖的弦圖有兩個不相鄰的單純點。

定理:無向圖是弦圖的充要條件是有完美消除序列。

最大勢演算法:貪心,維護每個節點的"勢" \(label[x]\) 表示已經加入序列的點中與 \(x\) 相鄰的數量。每次將最大勢的點加入序列開頭並更新與其相鄰的點的 \(label[x]\),得到一個消除序列。

判斷完美消除序列演算法:對於排列 \(v_1,v_2,\dots,v_n\),對於所有 \(i\in[1,n]\cap\N\),與 \(v_i\) 相鄰的點為 \(v_{j_1},\dots,v_{j_k}\)\(v_{j_1}\)\(v_{j_2},\dots,v_{j_k}\) 相鄰,則 \(v\) 為完美消除序列。複雜度線性。

判斷絃圖演算法:用最大勢演算法求出一個消除序列,判斷它是否完美。複雜度線性。【正確性證明?】

計算團數:設 \(p(v)\)\(v\) 在完美消除序列中的位置,\(N(v)=\{w|w與v相鄰且p(w)>p(v)\}\),則弦圖的所有極大團形如 \(v\cup N(v)\)

計算最小染色方案:由計算團數的方案可得,按照完美消除序列的逆序貪心,得到顏色數為團數的方案,又因為團數\(\le\)色數,所以即為最小染色方案。

計算最大獨立集:按照完美消除序列的順序貪心。

計算最小團覆蓋方案:設 \(\{p_1,\dots,p_t\}\) 為最大獨立集,則 \(\{p_1\cup N(p_1),\dots,p_t\cup N(p_t)\}\) 為一種方案。

推論:弦圖中,團數\(=\)色數,|最大獨立集|=|最小團覆蓋|。

區間圖

定義:給定 \(n\) 個區間 \((l_i,r_i)\),定義相交圖為每個點代表一個區間,兩個點相連當且僅當這兩個區間相交。無向圖是區間圖當且僅當存在若干個區間的相交圖是它。

定理:區間圖是弦圖。按右端點排序得到的是完美消除序列。

300iq Contest 3

A Airplane Cliques

題目描述:給定一棵 \(n\) 個點的樹和自然數 \(x\),對於 \(k\in[1,n]\cap\N\),求滿足其中任意兩個點之間的距離 \(\le x\)\(k\) 元點集的數量\(\bmod 998244353\)

資料範圍:\(n\le3\times10^5,x<n\)

構造無向圖 \(G=(V,E)\),其中 \((u,v)\in E\Leftrightarrow dis(u,v)\le x\)。則 \(G\) 是弦圖,以任意點為根的任意 bfs 序是完美刪除序列的逆序。列舉這個團在完美刪除序列中最靠後的點 \(v\),則這個團是 \(v\cup N(v)\) 的子集。設 \(a_v=|N(v)|\),則答案就是 \(\sum\binom{a_v}{k-1}\),計算它可以使用 FFT 優化卷積。計算 \(a_i\) 據說可以用各種方法而我只會澱粉質,於是複雜度 \(O(n\log^2n)\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
namespace {
const int N = 311111, M = 1<<20, mod = 998244353;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
void qmo(int &x){x += (x >> 31) & mod;}
int ksm(int a, int b){
	int res = 1;
	for(;b;b >>= 1, a = (LL) a * a % mod) if(b & 1) res = (LL) res * a % mod;
	return res;
}
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
inline bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
}
int n, d, tot, rt, siz[N], dep[N], p[N], num[N], ans[N];
vector<int> E[N];
void dfs(int x, int f = 0){for(Rint v : E[x]) if(v != f){dep[v] = dep[x] + 1; dfs(v, x);}}
bool cmp(int a, int b){return dep[a] < dep[b];}
int tr[N], cnt;
void upd(int p, int v){for(;p <= n;p += p & -p) tr[p] += v;}
int qry(int p){int r = 0; for(;p;p -= p & -p) r += tr[p]; return r;}
pii tmp[N];
bool vis[N];
void getrt(int x, int f = 0){
	siz[x] = 1; bool t = true;
	for(Rint v : E[x]) if(v != f && !vis[v]){getrt(v, x); if((siz[v]<<1)>tot) t = false; siz[x] += siz[v];}
	if(t && tot<=(siz[x]<<1)) rt = x;
}
void dfs2(int x, int dep = 0, int f = 0){
	tmp[cnt++] = MP(dep, x);
	for(Rint v : E[x]) if(v != f && !vis[v]) dfs2(v, dep+1, x);
}
void calc(int x, int op, int lim){
	int r = 0; cnt = 0; dfs2(x); sort(tmp, tmp + cnt);
	for(Rint l = cnt-1;~l;-- l){
		while(r < cnt && tmp[l].fi + tmp[r].fi <= lim) upd(num[tmp[r++].se], 1);
		ans[tmp[l].se] += op * qry(num[tmp[l].se]-1);
	}
	for(Rint i = 0;i < r;++ i) upd(num[tmp[i].se], -1);
}
void solve(int x){
	vis[x] = true; calc(x, 1, d);
	for(Rint v : E[x]) if(!vis[v]) calc(v, -1, d-2);
	for(Rint v : E[x]) if(!vis[v]){tot = siz[v]; getrt(rt = v, x); solve(rt);}
}
int fac[N], inv[N], A[M], B[M], lim, rev[M], w[2][M];
void init(int n){
	fac[0] = 1;
	for(Rint i = 1;i <= n;++ i) fac[i] = (LL) fac[i-1] * i % mod;
	inv[n] = ksm(fac[n], mod - 2);
	for(Rint i = n;i;-- i) inv[i-1] = (LL) inv[i] * i % mod;
}
void calrev(int len){
	int L = -1; lim = 1;
	while(lim <= len){lim <<= 1; ++ L;}
	for(Rint i = 0;i < lim;++ i) rev[i] = (rev[i>>1]>>1) | ((i&1)<<L);
	for(Rint mid = 1;mid < lim;mid <<= 1){
		w[0][mid] = w[1][mid] = 1; int Wn = ksm(3, (mod - 1) / (mid << 1));
		for(Rint i = 1;i < mid;++ i) w[0][mid+i] = (LL) w[0][mid+i-1] * Wn % mod;
		for(Rint i = 1;i < mid;++ i) w[1][mid+i] = mod - w[0][(mid<<1)-i];
	}
}
void NTT(int *A, int op){
	for(Rint i = 0;i < lim;++ i) if(i < rev[i]) swap(A[i], A[rev[i]]);
	for(Rint mid = 1;mid < lim;mid <<= 1)
		for(Rint i = 0;i < lim;i += mid<<1)
			for(Rint j = 0;j < mid;++ j){
				int y = (LL) A[mid + i + j] * w[op][mid + j] % mod;
				qmo(A[mid + i + j] = A[i + j] - y); qmo(A[i + j] += y - mod);
			}
	if(op){
		int inv = ksm(lim, mod - 2);
		for(Rint i = 0;i < lim;++ i) A[i] = (LL) A[i] * inv % mod;
	}
}
int main(){
	read(n); read(d);
	for(Rint i = 1, u, v;i < n;++ i){
		read(u); read(v); E[u].PB(v); E[v].PB(u);
	} dfs(1);
	for(Rint i = 1;i <= n;++ i) p[i] = i;
	sort(p + 1, p + n + 1, cmp);
	for(Rint i = 1;i <= n;++ i) num[p[i]] = i;
	tot = n; getrt(1); solve(rt); init(n); calrev(n-1<<1);
	for(Rint i = 1;i <= n;++ i) ++ A[ans[i]];
	for(Rint i = 0;i < n;++ i) A[i] = (LL) A[i] * fac[i] % mod;
	for(Rint i = 0;i < n;++ i) B[i] = inv[n-1-i];
	NTT(A, 0); NTT(B, 0);
	for(Rint i = 0;i < lim;++ i) A[i] = (LL) A[i] * B[i] % mod;
	NTT(A, 1);
	for(Rint i = 0;i < n;++ i) printf("%lld ", (LL) A[i+n-1] * inv[i] % mod); 
}

B Best Tree

題目描述:給定長為 \(n\) 的正整數序列 \(d_i\),求在所有 \(n\) 個點的,第 \(i\) 個節點的度數為 \(d_i\) 的樹中,最大匹配個數的最大值。\(t\) 組資料。

資料範圍:\(n\ge 2,\sum n\le 2\times 10^5\)

眾所周知,樹上最大匹配的做法就是從葉子往上貪心,所以每次選取一個 \(d_u>1\) 的點,然後刪去 \(d_u-1\) 個葉子(易得必定可行),匹配個數+1。於是當 \(n=2\) 時答案為 \(1\),否則答案為 \(\min(\sum[d_i>1],\frac{n}{2})\)

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
#define PB push_back
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 200003, mod = 998244353;
template<typename T>
void read(T &x){
	int ch = getchar(); x = 0; bool f = false;
	for(;ch < '0' || ch > '9';ch = getchar()) f |= ch == '-';
	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
	if(f) x = -x;
}
int t, n, s, x;
int main(){
	read(t);
	while(t --){
		read(n); s = 0;
		for(Rint i = 0;i < n;++ i){read(x); s += x > 1;}
		if(n == 2) puts("1");
		else printf("%d\n", min(s, n>>1));
	}
}

C Cells Blocking

題目描述:給定 \(n\times m\) 的矩陣,每個元素是障礙或空地,求有多少種將其中兩個空地變為障礙的方案,使得你無法從 \((1,1)\) 開始,每次向下或向右走一格,到達 \((n,m)\)

資料範圍:\(n,m\le 3000\)

首先特判掉本來就不可達的情況。

這種 \(2/4\) 方向的路徑聯通性問題,一般可以考慮極端路徑。考慮其中的兩條路徑:能向下則向下 和 能向右則向右。如果 block 掉這兩條路徑的交點,那麼一定不可達,否則這兩條路徑必須各 block 一格,列舉其中一格更新路徑交點即可。時間複雜度 \(O((n+m)^2)\)

D Disjoint LIS

本題是提交答案題(?)

題目描述:對於所有正整數 \(n\le 75\),求可以選出兩個不交的 LIS 的長為 \(n\) 的排列的數量\(\bmod 998244353\)

基礎樣表理論練習題。一個排列與兩個相同規模的標準樣表一一對應。題目條件即為樣表的前兩行一樣長。暴力列舉整數拆分即可。時間複雜度 \(O(np(n))\),其中 \(p(n)\)\(n\) 的整數拆分方案數。

#include<cstdio>
using namespace std;
const int ans[] = {0, 1, 1, 5, 26, 132, 834, 6477, 56242, 532712, 5561524, 64098432, 806184757, 948233294, 989748344, 143602156, 634648323, 421103776, 925864750, 797907052, 912638617, 772780014, 386111399, 327774735, 845702948, 791619521, 878631164, 37336541, 165629590, 969299909, 774782650, 860045241, 866378903, 492112739, 824431980, 949969944, 970248926, 278772092, 118910029, 190687907, 976616702, 231648679, 722078798, 677622359, 166145100, 322790432, 114852733, 809056236, 133146712, 978647108, 363079147, 447411358, 261833982, 586593658, 543018772, 281510931, 509498959, 499059113, 216456979, 892597175, 288388296, 782889761, 259994934, 668319048, 447568639, 974154792, 434978721, 809640594, 541844549, 9840250, 275431656, 31040948, 964592956, 743394989, 89362287};
int main(){int n; scanf("%d", &n); printf("%d\n", ans[n-1]);}

E Easy Win

題目描述:給定長為 \(n\) 的正整數序列 \(a_i\),對於所有 \(x\in[2,n+1]\cap\Z\),求 \(\bigoplus\limits_{i=1}^n(a_i\bmod x)\) 是否為 \(0\)

資料範圍:\(a_i\le n\le 5\times 10^5\)

對於列舉 \(x\)\(\lfloor\frac{a_i}x\rfloor\),再分別考慮每一位 \(c\),計算可以使用字首和優化,時間複雜度 \(O(n\log^2n)\)

H Horrible Cycles

題目描述:給定 \(2\times n\) 個點的二分圖,左邊第 \(i\) 個點連向右邊前 \(a_i\) 個點。求簡單環的數量\(\bmod 998244353\)

資料範圍:\(n\le 5000,1\le a_i\le n\)

這種 dp 見過好多次但還是忘了,就是維護連通性的時候,以點和連通性為狀態轉移。

將左邊的點按照 \(a_i\) 排序,則右邊 \((a_{i-1},a_i]\) 連向左邊 \([i,n]\)。更新順序是 \(i=1\rightarrow n\),加入右邊 \((a_{i-1},a_i]\) 再加入左邊第 \(i\) 個點,\(F_{?,j}\) 表示當前加入 \(?\) 個點,右邊的點形成 \(j\) 個連續段。

300iq Contest 1

A Angle Beats

題目描述:給定 \(n\times m\) 的表格,每個元素可能是 +*. 中的一種。你可以擺放一些大小為 \(3\) 的塊在上面:將 \(1\times 3\) 矩形的中心放在 + 上,或將 L 形中心放在 *+ 上。這些塊不能相交,求最多能放多少個塊,並構造方案。

資料範圍:\(2\le n,m\le 100\)

一看就是個二分圖最大匹配,可如何建圖呢?

若只有 +,則可以將每個 + 拆成兩個點,分別都向四周連邊,答案就是最大匹配減去 + 的個數。

考慮上 * 之後,* 就可以改為兩個點分別向豎直、水平方向連邊。智慧建圖

B Best Subsequence

題目描述:給定長為 \(n\) 的正整數序列 \(a\),求

\[\min\{\max\{a_{i_j}+a_{i_{(j+1)\text{mod} k}}|j\in[0,k)\}|1\le i_0<\dots<i_{k-1}\le n\} \]

資料範圍:\(3\le k\le n\le 2\times 10^5,a_i\le 10^9\)

不會貪心?啥也不會

每次刪掉最大的 \(a_{i_j}+a_{i_{j+1}}\) 中較大的數。時間複雜度 \(O(n\log n)\)

C Cool Pairs

題目描述:給定兩個長為 \(n\) 的排列 \(p,q\) 和自然數 \(k\),構造兩個長為 \(n\) 的正整數序列 \(a,b\) 滿足:

  • \(-n\le a_i,b_i\le n\)
  • \(a_{p_1}\le a_{p_2}\le\dots\le a_{p_n}\)\(b_{q_1}\le b_{q_2}\le\dots\le b_{q_n}\)
  • \(|\{(i,j)|i<j\and a_i<b_j\}|=k\)

資料範圍:\(n\le 3\times 10^5,k\le\binom n2\)

這是一個構造順序對的過程,用 BIT 維護桶即可,時間複雜度 \(O(n\log n)\)

E Expected Value

題目描述:隨機遊走的步數期望。

資料範圍:\(n\le 3000\),平面圖。

步數 \(>i\) 的概率是長度 \(\le n\) 的遞推數列,BM 即可。時間複雜度 \(O(nm)\)

Tutte Theorem

無向圖 \(G=(V,E)\) 有完美匹配 \(\Leftrightarrow\forall U\subseteq V,o(V-U)\le|U|\)\(o(X)\) 表示子圖 \(X\) 的奇連通塊數)

證明:不會

Tutte-Berge formula

無向圖 \(G=(V,E)\) 的最大匹配數為 \(\frac{1}{2}\min\limits_{U\subseteq V}\{|U|+|V|-o(V-U)\}\)

G Graph Counting

題目描述:給定正整數 \(n\),求沒有完美匹配,但任意加上一條邊之後就會有完美匹配的 \(2n\) 個點的無標號無向圖數量\(\bmod 998244353\)

資料範圍:\(n\le5\times10^5\)

根據 Tutte-Berge formula,題意條件可以等價為 \(\min_{U\subseteq V}\{|U|-o(V-U)\}=-2\),且對於所有這樣的點集 \(U\),內部所有點的度數都是 \(2n-1\),外部都是大小為奇數的聯通塊。可得答案就是 \(p(n+1)-1\),其中 \(p(n)\) 表示 \(n\) 的整數拆分方案數,使用五邊形數定理和多項式求逆,時間複雜度 \(O(n\log n)\)

I Interesting Graph

題目描述:給定 \(n\) 個點 \(m\) 條邊的無向圖,對於所有 \(k\in[1,n]\cap\N\),求 \(k\) 種顏色的染色方案。

資料範圍:\(n,m\le 10^5\),點雙連通分量大小 \(\le 7\)

看見這個條件就知道,先預處理大小為 \(s\) 的點雙連通分量在固定根時的方案數,是關於 \(k\)\(s-1\) 次多項式。將這些多項式乘起來再乘上 \(k\) 的連通塊個數次方,多點求值即可得到答案。

計算每個點雙連通分量時,可以用暴力列舉+插值。

J Jealous Split

題目描述:給定長為 \(n\) 的自然數序列 \(a\) 和正整數 \(k\),你需要將其分割為 \(k\) 段,使得相鄰兩段的和之差 \(\le\) 這兩段的最大值。需判斷無解。

資料範圍:\(3\le k\le n\le 10^5\)

-K Knowledge

題目描述:給定字串 \(S\) 和自然數 \(x\),你可以對其做如下操作:加入/刪除子串aa/bbb/ababab。求能得到多少個長為 \(x\) 的字串。

資料範圍:\(|S|\le 3\times 10^5,x\le 10^9,\Sigma=\{a,b\}\)

發現 \(bb\rightarrow abababbb\rightarrow ababa\)\(aa\rightarrow\varnothing\)\(ababab\rightarrow\varnothing\),於是我們一定能將任意一個字串通過上述操作變為 \(12\) 種串(\(ababa\) 的所有字首以及 \(b\) 與它們的拼接)。

-CF1408I Bitwise Magic

題目描述:給定長為 \(n\) 的元素互不相同的正整數序列 \(a\) 和正整數 \(k,c\),需要對這個序列操作 \(k\) 次,每次隨機選擇一個 \(a_i\),將其 \(-1\)。對於所有 \(x\in[0,2^c)\) 輸出最後序列的異或和為 \(x\) 的概率\(\bmod 998244353\)

資料範圍:\(k,c\le 16,a_i\in [k,2^c)\)

為何能觀察到性質:\(|\{(x\oplus(x-1),x\oplus(x-2),\dots,x\oplus(x-k))|x<2^c\}|=O(ck)\),取極限資料時為 \(192\)

證明:這個序列只與 \(x\) 從最低 \(1\) 開始的最後 \(O(\log k)\) 位有關。

\(t=\bigoplus\limits_{i=1}^na_i\),考慮答案在 \(t\) 上的改變數。

使用 EGF 統計操作序列的數量,設 \(d_{i,j}=a_i\oplus(a_i-j)\),要求的就是

\[k![y^k]\prod_{i=1}^n(1+\sum_{j=0}^k\frac{x^{d_{i,j}}y^j}{j!}) \]

其中 \(x\) 是異或卷積,\(y\) 是加法卷積。

CF1148H Holy Diver

題目描述:給定一個初始為空的陣列,每次操作給定 \(a,l,r,k\),在陣列末尾插入 \(a\),再求 \(|\{(i,j)|l\le i\le j\le r\and\text{mex}(a[i:j])=k\}|\)。強制線上。

資料範圍:\(n\le 2\times 10^5\)

LOJ2553「CTSC2018」暴力寫掛

題目描述:給定兩棵 \(n\) 個點的以 \(1\) 為根的樹 \(T_1,T_2\),設 \(dep_x,dep'_x\) 分別表示 \(x\)\(T_1,T_2\) 中的深度,\(lca(x,y),lca'(x,y)\) 分別表示 \(x,y\)\(T_1,T_2\) 中的 LCA。求

\[\max\{dep_x+dep_y-dep_{lca(x,y)}-dep'_{lca'(x,y)}|1\le x\le y\le n\} \]

資料範圍:\(n\le 366666\)

之前一直都沒真正學過邊分治,現在來搞一搞。

首先列舉 \(T_2\) 中的 \(lca\),變為了在第二棵樹的子樹中選出第一棵樹中 \(dep_x+dep_y-dep[lca(x,y)]=\frac{1}{2}(dep_x+dep_y+dis_{x,y})\) 的最小值。

\(lca\) 消掉是為了將 \(dis\) 從分治中心割開,對每個節點維護到邊分樹上祖先的距離,使用類似線段樹合並的方法計算 \((x,y)\) 貢獻並滿足 \(T_2\) 中的 \(lca\) 的限制。時間復雜度 \(O(n\log n)\)

LOJ2261「CTSC2017」金鑰

\(A\) 看作 \(1\)\(B\) 看作 \(-1\)。求出字首和,找到第 \(S\) 大值即可(若有相同的先考慮後面的)。

(所以結論怎麼證aaa)

#include<bits/stdc++.h>
using namespace std;
const int N = 2e7+5;
template<typename T>
inline bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
int n, k, S, tmp, i, mx, p[N], q[N], c[N<<1];
short seed;
int getrand(){return seed = ((seed * 12321) ^ 9999) & 32767;}
int solve(int x){
    int o = mx;
    for(;x >= c[o];x -= c[o--]);
    for(i = n;i;-- i) if(!p[i] && q[i] == o && !x--) return i;
}
int main(){
    scanf("%d%hd%d", &k, &seed, &S); n = k << 1 | 1;
    for(i = 1;i <= n;++ i) tmp += p[i] = getrand() >> 7 & 1; i = 1;
    while(tmp > k){while(!p[i]) ++ i; p[i] = 0; -- tmp;}
    while(tmp < k){while(p[i]) ++ i; p[i] = 1; ++ tmp;}
    q[0] = n;
    for(i = 1;i <= n;++ i){
        q[i] = q[i-1] + 1;
        if(!p[i]){++ c[q[i] -= 2]; chmax(mx, q[i]);}
    }
    printf("%d\n%d\n%d\n", solve(0), solve(S), solve(k - S));
}

LOJ2262「CTSC2017」網路

LOJ2263「CTSC2017」遊戲

\[P(A|B)=\frac{P(A)P(B|A)}{P(B)} \]

求每局遊戲小 R 獲勝的概率之和。對於一個未知局面,設第 \(i\) 局左邊第一個已知局面為 \(l\),右邊第一個已知局面為 \(r\)。設事件 \(L,R\)\(l,r\) 獲勝者與已知相符。

\[\begin{align} P(X|L,R)&=\frac{P(L,R|X)P(X)}{P(L,R)} \\ &=\frac{P(L|X)P(R|X)P(X)}{P(L,R)} \\ &=\frac{P(X|L)P(R|X)}{P(R|L)} \end{align} \]

分子和分母都可以使用線段樹維護矩陣連乘積來計算,時間複雜度 \(O((n+m)\log n)\)

LOJ6405「ICPC World Finals 2018」征服世界

題目描述:給定 \(n\) 個點的樹,邊帶權,初始第 \(i\) 個點有 \(x_i\) 個軍隊,最終要使第 \(i\) 個點至少有 \(y_i\) 個軍隊,求移動路程之和的最小值。

資料範圍:\(n\le 2.5\times 10^5,\sum y_i\le\sum x_i\le 10^6,0\le c\le 10^6\)

肝敗嚇瘋...

這實際上是一個二分圖匹配,考慮 dfs 列舉匹配的 lca,匹配的代價就是 \(dis(u,v)=dep_u+dep_v-2dep_{lca}\),因為匹配的初始位置和目標位置在同一個子樹時不優,所以不用考慮這種情況。

將每一個初始位置的權值 \(-\infty\),這樣每個初始位置就會被強制匹配。

一對已經在 \(z\) 處匹配的初始位置和目標位置不會同時反悔,所以元素入堆的總次數為 \(O(\sum x_i)\) 級別。

合併相同元素,使用可並堆維護,時間複雜度 \(O(n\log n)\)

#include<bits/stdc++.h>
#include<ext/pb_ds/priority_queue.hpp>
#define Rint register int
using namespace std;
typedef long long LL;
const int N = 250003;
const LL inf = 1e12;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int n, head[N], to[N<<1], nxt[N<<1], w[N<<1], a[N], b[N], s; LL ans;
void add(int a, int b, int c){
    static int cnt = 0;
    to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt; w[cnt] = c;
}
struct Node {
    LL val; mutable int cnt;
    Node(LL v = 0, int c = 0): val(v), cnt(c){}
    inline bool operator < (const Node &o) const {return val > o.val;}
}; __gnu_pbds::priority_queue<Node> p[N], q[N];
void dfs(int x, int f = 0, LL d = 0){
    p[x].push(Node(d, a[x])); q[x].push(Node(d - inf, b[x]));
    for(Rint i = head[x];i;i = nxt[i]) if(to[i] != f){
        dfs(to[i], x, d + w[i]); p[x].join(p[to[i]]); q[x].join(q[to[i]]);
    }
    while(!p[x].empty() && !q[x].empty()){
        Node t1 = p[x].top(), t2 = q[x].top();
        int cc = min(t1.cnt, t2.cnt); LL tmp = t1.val + t2.val - (d<<1);
        if(tmp >= 0) break; ans += cc * tmp;
        p[x].push(Node(t1.val - tmp, cc));
        q[x].push(Node(t2.val - tmp, cc));
        p[x].top().cnt -= cc; if(!p[x].top().cnt) p[x].pop();
        q[x].top().cnt -= cc; if(!q[x].top().cnt) q[x].pop();
    }
}
int main(){
    read(n);
    for(Rint i = 1, u, v, c;i < n;++ i){
        read(u); read(v); read(c); add(u, v, c); add(v, u, c);
    }
    for(Rint i = 1;i <= n;++ i){read(a[i]); read(b[i]); s += b[i];}
    dfs(1); printf("%lld\n", ans + s * inf);
}

LOJ6406「ICPC World Finals 2018」綠寶石之島

題目描述:給定正整數 \(n,d,r\),長為 \(n\) 的正整數序列 \(a\) 初始為 \(0\),每次操作以 \(a_i+1\) 的權重隨機一個整數 \(i\in[1,n]\),然後將 \(a_i\)\(1\),求 \(d\) 次操作之後 \(a\) 中前 \(r\) 大的值的期望。

資料範圍:\(n,d\le 500,r\le n\)

對於一種局面 \(\sum a_i=d\),出現這種局面的情況數是 \(\frac{d!}{\prod a_i!}\times\prod a_i!=d!\),所以出現每種局面的概率相同!

然後就可以直接 dp,設 \(f_{i,j}\) 表示方案數,\(g_{i,j}\) 表示答案,其中 \(i,j\) 分別表示元素個數和 \(\sum a_i\)。列舉當前有 \(k\) 個最大值,然後減去 \(1\) 變為子問題。

\[\begin{aligned} f_{i,j}&=\sum_{k=0}^{\min(i,j)}\binom ikcnt_{k,j-k} \\ g_{i,j}&=\sum_{k=0}^{\min(i,j)}\binom ik(sum_{k,j-k}+\min(k,r)cnt_{k,j-k}) \end{aligned} \]

時間複雜度 \(O(n^2d)\)

#include<bits/stdc++.h>
#define Rint register int
using namespace std;
const int N = 503;
double c[N][N], f[N][N], g[N][N];
int n, d, r;
int main(){
    scanf("%d%d%d", &n, &d, &r); c[0][0] = f[0][0] = 1;
    for(Rint i = 1;i <= n;++ i){
        c[i][0] = 1;
        for(Rint j = 1;j <= i;++ j) c[i][j] = c[i-1][j-1] + c[i-1][j];
    }
    for(Rint i = 1;i <= n;++ i)
        for(Rint j = 0;j <= d;++ j)
            for(Rint k = 0;k <= i && k <= j;++ k){
                f[i][j] += c[i][k] * f[k][j-k];
                g[i][j] += c[i][k] * (g[k][j-k] + min(k,r) * f[k][j-k]);
            }
    printf("%.6lf\n", g[n][d] / f[n][d] + r);
}

LOJ6407「ICPC World Finals 2018」跳過罪惡

模擬題,程式碼咕了。

LOJ6410「ICPC World Finals 2018」單割故障

易(nan)得,答案 \(\le 2\)

將矩形邊界看作一個環,答案 \(=1\) 當且僅當存在一個區間滿足其包含所有區間的恰好一個端點。直接列舉即可。

時間複雜度 \(O(n\log n)\)你學廢了麼?

#include<bits/stdc++.h>
#define Rint register int
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef pair<int, int> pii;
const int N = 1000003;
template<typename T>
inline void read(T &x){
    int ch = getchar(); x = 0;
    for(;ch < '0' || ch > '9';ch = getchar());
    for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
}
int n, w, h, cnt[N], now; pii a[N<<1];
int get(int x, int y){
    if(!y) return x;
    if(x == w) return w + y;
    if(y == h) return (w<<1) + h - x;
    return (w<<1) + (h<<1) - y;
}
void print(double p){
    if(p <= w) printf("%.1lf 0 ", p);
    else if(p <= w + h) printf("%d %.1lf ", w, p - w);
    else if(p <= (w<<1) + h) printf("%.1lf %d ", (w<<1) + h - p, h);
    else printf("0 %.1lf ", (w<<1) + (h<<1) - p)
}
int main(){
    read(n); read(w); read(h);
    for(Rint i = 0, x, y;i < (n<<1);++ i){
        read(x); read(y); a[i] = MP(get(x, y), i>>1);
    }
    sort(a, a + (n<<1));
    for(Rint i = 0;i < n;++ i) now += !cnt[a[i].se]++;
    for(Rint i = 0;i < n;++ i){
        if(now == n){puts("1"); print(a[i].fi - 0.5); print(a[i+n].fi - 0.5); return 0;}
        now -= !--cnt[a[i].se]; now += !cnt[a[i+n].se]++;
    }
    puts("2"); print(0.5); print(w + h + 0.5); putchar('\n'); print(w + 0.5); print((w<<1) + h + 0.5);
}

LOJ6470「ICPC World Finals 2017」機場構建

答案線段必定經過至少兩個頂點(

直接暴力,時間複雜度 \(O(n^3)\)