Educational Codeforces Round 137 (Rated for Div. 2)練習筆記

\(\text{A. Password}\)

\(\text{Monocarp}\) 的手機密碼由 \(4\) 位數字組成(首位可以爲 \(0\) ),其中只包含兩個不同的數字,並且這兩個數字都出現了兩次。現在他告訴你他的密碼肯定不會包含哪些數字,讓你求出有多少種可能的密碼。多組數據。
其實答案就是 \({10-n\choose 2}{4\choose 2}\)

int T, n;
int main(){
	T = rd();
	while(T--){
		n = rd();
		fp(i, 1, n)rd();
		printf("%d\n", (10-n)*(9-n)*3);
	}
	return 0;
}

\(\text{B. Permutation Value}\)

定義一個排列的價值爲:有多少個區間,使得這個區間也是一個排列。給定一個 \(n(n\leq 50)\) ,要求輸出任意一個價值最小的長度爲 \(n\) 的排列。多組數據。
如果一個區間也是排列,並且長度 \(>1\) ,那麼它肯定包含 \(1,2\) 。所以只需要讓 \(1\) 在最左邊, \(2\) 在最右邊,就可以使這個排列的價值最小。

int T, n;
int main(){
	T = rd();
	while(T--){
		n = rd();
		printf("1 "); fp(i, 3, n)printf("%d ", i); printf("2\n");
	}
	return 0;
}

\(\text{C. Save the Magazines}\)

從左到右排列了 \(n(n\leq 2\times 10^5)\) 個盒子,第 \(i\) 個盒子裏有 \(a_i\) 本雜誌。有的盒子上有一個蓋子,有的則沒有。一次操作爲將某個盒子上的蓋子移到左邊的盒子上,每個蓋子最多隻能被移動一次。求若干次操作之後,最多有多少本雜誌可以被蓋子蓋住。多組數據。
很好想的做法是直接 \(\text{dp}\) 。設 \(f_{i,0}\) 表示不移動第 \(i\) 個盒子上的蓋子,第 \(1\)~\(i\) 個盒子最多可以有多少本雜誌被蓋住,設 \(f_{i,1}\) 表示移動第 \(i\) 個盒子上的蓋子(如果有的話),第 \(1\)~\(i\) 個盒子最多可以有多少本雜誌被蓋住。
如果第 \(i\) 個盒子一開始沒有蓋子,則 \(f_{i,0} = \max\{f_{i-1,0},f_{i-1,1}\}\)
否則, \(f_{i,0} = \max\{f_{i-1,0},f_{i-1,1}\}+a_i,f_{i,1}=f_{i-1,lid_{i-1}}+a_{i-1}\) ,其中 \(lid_i\) 表示第 \(i\) 個盒子上一開始有沒有蓋子。

cint maxn = 200010;
int T, n, lid[maxn], a[maxn];
unsigned int f[maxn][2];
int main(){
	T = rd();
	while(T--){
		n = rd();
		fp(i, 1, n)scanf("%1d", &lid[i]);
		fp(i, 1, n)a[i] = rd(), f[i][0] = f[i][1] = 0;
		if(lid[1])f[1][0] = a[1];
		fp(i, 2, n){
			if(!lid[i])f[i][0] = max(f[i-1][0], f[i-1][1]);
			else{
				f[i][0] = max(f[i-1][0], f[i-1][1])+a[i];
				f[i][1] = (lid[i-1] ? f[i-1][1] : f[i-1][0]) + a[i-1];
			}
		}
		printf("%u\n", max(f[n][0], f[n][1]));
	}
	return 0;
}

\(\text{D. Problem with Random Tests}\)

給定一個長度爲 \(n(n\leq 10^6)\)\(01\) 串,要求截取任意兩段(可以相交),使得這兩段或運算結果最大。數據隨機
或運算具有貪心的性質,所以讓第一段取到最左邊的 \(1\)\(01\) 串的右端點顯然是最優的。
然後考慮一種暴力,枚舉第二段的左端點,然後暴力算,複雜度是 \(\text O(n^2)\) 的。
考慮第一個優化,首先最優的方法肯定是選取與第一個 \(1\) 在同一塊(全是 \(1\) 的一段)裏的 \(1\) 作爲第二段的左端點,因爲這樣纔可能通過調整右端點的位置讓這個 \(1\) 與第一段的第一個 \(0\) 進行或運算。
這樣只是常數上進行了優化。再考慮第二個優化,怎麼使用隨機數據這個條件?
我們將使用第一個優化過後所有的 \(1\) 作爲備選項,並將它們與第一段的第一個 \(0\) 對齊。然後從左到右枚舉第一段裏的所有 \(0\) ,看每個備選的第二段能否在對應的位置上是 \(1\) 。如果有至少一個備選項滿足條件,則將所有不滿足條件的備選項去掉;否則全部保留,繼續往後枚舉第一段的 \(0\) 。最後剩下的就是最優的第二段。
因爲數據隨機,所以每次都期望去掉一半的備選項,所以這樣做的複雜度是 \(\text O(n\log n)\) 的。

cint maxn = 1000010;
int n, a[maxn], pos[maxn], cnt, f[maxn], tot;
int main(){
	n = rd();
	fp(i, 1, n)scanf("%1d", &a[i]);
	int now = 0; fp(i, 1, n)if(a[i]){ now = i; break; }
	if(!now)return putchar('0'), 0;
	fp(i, now+1, n)if(!a[i])pos[++cnt] = i;
	fp(i, 1, pos[1]-1)if(a[i])f[++tot] = i;
	fp(i, 1, cnt){
		int fl = 0, _tot = 0;
		fp(j, 1, tot)if(f[j] + pos[i]-pos[1] <= n && a[f[j] + pos[i]-pos[1]]){ fl = 1; break; }
		if(!fl)continue;
		fp(j, 1, tot)if(f[j] + pos[i]-pos[1] <= n && a[f[j] + pos[i]-pos[1]])f[++_tot] = f[j];
		tot = _tot;
	}
	int tmp = 0;
	fp(i, now, n){
		if(a[i])putchar('1');
		else{
			++tmp;
			putchar(a[f[1]+pos[tmp]-pos[1]] ? '1' : '0');
		}
	}
	return 0;
}

\(\text{E. FTL}\)

有兩門激光炮,各自有一個威力 \(d_1,d_2(d_i\leq 5000)\) 和充能時間 \(p_1,p_2(p_i\leq 10^{12})\) 。敵人擁有 \(h(h\leq 5000)\) 滴血和 \(s(s\leq \min\{d_1,d_2\})\) 點防禦,每次在某一門激光炮充能完畢後使用它,也可以等兩門激光炮都充能後一起發射,造成的傷害爲:這次使用的激光炮的威力(或威力之和)\(-s\) 。求出最少需要多少時間可以擊殺敵人。
由於兩門炮分別充能,所以貪心是不好做的……
考慮 \(\text{dp}\) 。設 \(dp_i\) 代表造成 \(i\) 點傷害最少需要多長時間。我們可以欽定最後一次攻擊爲兩門炮一起攻擊,因爲如果不是一起攻擊的,我們可以讓較早發射的炮等到另一門炮充能完畢過後一起發射,而且這樣造成的傷害更高。那我們可以枚舉第一門炮單獨發射多少次過後進行一次兩門炮齊射,再枚舉第二門炮單獨發射多少次過後進行兩門炮齊射。複雜度大概就是 \(\text{O(h^2)}\) 的。

cint maxn = 5010;
int p1, p2, h, s;
LL t1, t2, f[maxn];
int main(){
	scanf("%d %lld", &p1, &t1);
	scanf("%d %lld", &p2, &t2);
	scanf("%d %d", &h, &s);
	memset(f, 0x3f, sizeof f), f[0] = 0;
	fp(i, 0, h){
		f[min(h, i+p1-s)] = min(f[min(h, i+p1-s)], f[i]+t1);
		f[min(h, i+p2-s)] = min(f[min(h, i+p2-s)], f[i]+t2);
		fp(j, 1, h){
			if(j*t1 >= t2){
				LL dam = 1ll*(j-1)*(p1-s) + (j*t1-t2)/t2*(p2-s) + p1+p2-s;
				f[min((LL)h, i+dam)] = min(f[min((LL)h, i+dam)], f[i]+j*t1);
			}
			if(j*t2 >= t1){
				LL dam = 1ll*(j-1)*(p2-s) + (j*t2-t1)/t1*(p1-s) + p1+p2-s;
				f[min((LL)h, i+dam)] = min(f[min((LL)h, i+dam)], f[i]+j*t2);
			}
		}
	}
	printf("%lld\n", f[h]);
	return 0;
}

\(\text{F. Intersection and Union}\)

給定 \(n(n\leq 3\times 10^5)\) 個區間 \([l_i,r_i](l_i,r_i\leq 3\times 10^5)\) 。區間之間的操作有三種, \(\bigcup,\bigcap,\bigoplus\) ,分別表示交、並、對稱差(並集減去交集)。記第 \(i\) 個區間爲 \(S_i\) ,定義一個操作序列 \(\{op_1,op_2,...,op_{n-1}\}\) 的價值爲 \(|(((S_1op_1S_2)op_2S_3)op_3S_4)...op_{n-1}S_n|\) 。求出所有操作序列的價值之和,對 \(998244353\) 取模。
考慮單獨算每個位置對答案的貢獻,即計算有多少種操作序列能使它留在最後的集合裏。對於位置 \(x\) ,設 \(f_{i,0}\) 表示進行了第 \(i-1\) 次操作後,\(x\) 不在當前集合裏的方案數; \(f_{i,1}\) 表示在當前集合裏的方案數。
如果 \(x\in[l_i,r_i]\) ,分別考慮三種操作,那麼有 \(f_{i,0} = f_{i-1,0}+f_{i-1,1},f_{i,1} = (f_{i-1,0}+f_{i-1,1})+f_{i-1,1}+f_{i-1,0}\)
如果 \(x\notin[l_i,r_i]\) ,那麼有\(f_{i,0}=f_{i-1,0}+(f_{i-1,0}+f_{i-1,1})+f_{i-1,0},f_{i,1} = f_{i-1,1}+f_{i-1,1}\)
那麼直接這樣暴力算就是 \(\text O(n^2)\) 的。
可以發現當 \(x\in[l_i,r_i]\)\([f_{i,0},f_{i,1}] = [f_{i-1,0},f_{i-1,1}]\times \left[ \begin{array}{cc|r} 1 & 2\\1&2 \end{array}\right]\)
\(x\notin[l_i,r_i]\) 時有 \([f_{i,0},f_{i,1}] = [f_{i-1,0},f_{i-1,1}]\times \left[ \begin{array}{cc|r} 3 & 0\\1&2 \end{array}\right]\)
所以我們可以把所有區間的端點進行排序,並且從左到右枚舉 \(x\) ,如果 \(x=l_i\) 或者 \(x=r_i+1\),則更新第 \(i\) 個區間的轉移矩陣,用線段樹維護一個全局積,即可做到 \(\text O(n\log n)\)

cint maxn = 300010, mod = 998244353;
int n, l[maxn], r[maxn], L, R, f[maxn][2], ans;
int p1[maxn], p2[maxn];
il bool cmp1(cint &x, cint &y){ return l[x] < l[y]; }
il bool cmp2(cint &x, cint &y){ return r[x] < r[y]; }
struct matrix{ int a[3][3]; }t[maxn<<2], m1, m2;
il matrix operator * (cn matrix &x, cn matrix &y){
	matrix ans;
	ans.a[1][1] = (1ll*x.a[1][1]*y.a[1][1] + 1ll*x.a[1][2]*y.a[2][1])%mod;
	ans.a[1][2] = (1ll*x.a[1][1]*y.a[1][2] + 1ll*x.a[1][2]*y.a[2][2])%mod;
	ans.a[2][1] = (1ll*x.a[2][1]*y.a[1][1] + 1ll*x.a[2][2]*y.a[2][1])%mod;
	ans.a[2][2] = (1ll*x.a[2][1]*y.a[1][2] + 1ll*x.a[2][2]*y.a[2][2])%mod;
	return ans;
}
void build(int d, int l, int r){
	if(l == r)return t[d] = m2, void();
	int md = l+r>>1;
	build(d<<1, l, md), build(d<<1|1, md+1, r);
	t[d] = t[d<<1]*t[d<<1|1];
}
void mdf(int d, int l, int r, cint &des, cn matrix &m){
	if(des == 1)return;
	if(l == r)return t[d] = m, void();
	int md = l+r>>1;
	if(des <= md)mdf(d<<1, l, md, des, m);
	else mdf(d<<1|1, md+1, r, des, m);
	t[d] = t[d<<1]*t[d<<1|1];
}
int main(){
	n = rd(), L = 3e5;
	fp(i, 1, n)l[i] = rd(), r[i] = rd();
	fp(i, 1, n)if(l[i] < L)L = l[i];
	fp(i, 1, n)if(r[i] > R)R = r[i];
	fp(i, 1, n)p1[i] = p2[i] = i;
	sort(p1+1, p1+1+n, cmp1), sort(p2+1, p2+1+n, cmp2);
	m1.a[1][1] = m1.a[2][1] = 1, m1.a[1][2] = m1.a[2][2] = 2;
	m2.a[1][1] = 3, m2.a[1][2] = 0,  m2.a[2][1] = 1, m2.a[2][2] = 2;
	build(1, 2, n);
	int now1 = 0, now2 = 0;
	fp(i, L, R){
		while(now1 < n && l[p1[now1+1]] == i)++now1, mdf(1, 2, n, p1[now1], m1);
		while(now2 < n && r[p2[now2+1]] == i-1)++now2, mdf(1, 2, n, p2[now2], m2);
		if(l[1] <= i && i <= r[1])ans = (ans + t[1].a[2][2])%mod;
		else ans = (ans + t[1].a[1][2])%mod;
	}
	printf("%d\n", ans);
	return 0;
}

\(\text{G. Antifibonacci Cut}\)

定義 \(01\)\(f\) 爲斐波那契串,其中 \(f_1=0,f_2 = 1,f_{n}=f_{n-1}+f_{n-2}\) ,其中 \(+\) 代表拼接。再定義 \(g(s)\) 表示把 \(s\) 分割成若干個子串,並且沒有任何子串是斐波那契串的方案數。現給出 \(n(n\leq 3000)\)\(01\)\(s_1,s_2,...,s_n\)\(|s_i|\leq 1000\) ),要求對於每個 \(i\) ,計算 \(g(s_1+s_2+...+s_i)\pmod {998244353}\)內存限制爲 \(4\text M\)
首先可以做的事情是把所有串都拼起來,然後考慮怎麼 \(\text{dp}\)
一種比較暴力的 \(\text{dp}\) 爲,設 \(dp_i\) 表示 \(g(s[1...i])\) 。若沒有非斐波那契串的限制,那麼 \(dp_i=\sum_{j=1}^{i-1}dp_j\) 。可以發現的一條性質爲, \(f_{i-2}\)\(f_i\) 的後綴,並且 \(f_{33}\) 的長度會大於 \(3\times 10^6\) 。所以我們可以用 \(\text{exKMP}\) 求出 \(s[1...i]\) 分別與 \(f_{32},f_{31}\)\(\text{LCS}\) 。以 \(f_{32}\) 爲例,假設它的 \(\text{LCS}\) 對應的是 \(f_k\) ,那麼令 \(dp_i\)-=\(\sum_{j=1}^kdp_{i-|f_j|}\) 即可。複雜度是 \(\text O((\sum s_i)\log)\)
然而內存限制使得這題連 \(10^6\) 的數組都開不下……
可以發現另外一些性質(默認不考慮 \(f_1\) ): \(f_{i-1}\)\(f_i\) 的前綴;任何一個斐波那契串的前綴都可以表示成若干個編號不連續的斐波那契串拼接而成的形式。
下證第二條性質:首先長度爲 \(1\) 的前綴就是 \(f_2=1\) ,長度爲 \(2\) 的前綴就是 \(f_3=10\) 。假設長度爲 \(1\)~\(n-1\) 的前綴都滿足這個性質,設 \(f_k\) 爲最長的長度小於等於 \(n\) 的斐波那契串。若 \(|f_k|=n\) ,那麼長度爲 \(n\) 時就成立。否則,由於 \(s[f_k+1...n]\) 必然是 \(f_{k-1}\) 的前綴,且長度必然小於 \(|f_{k-1}|\) ,所以能表示成若干個編號不連續的斐波那契串的拼接,且編號最大的串編號不超過 \(k-2\)
爲了求出斐波那契串長度爲 \(n\) 的前綴由哪些斐波那契串拼接而成,可以參考下面的代碼:

fb(i, 32, 1)if(n > fib[i])n -= fib[i];

有了這些性質之後,假設現在計算了 \(dp_{i-1}\) ,我們可以維護 \(s[1...i-1]\) 有哪些長度的斐波那契串前綴的後綴,於是得到若干個二元組: \((l,dp_{i-l})\) 。考慮計算 \(dp_i\) 時怎麼維護這些二元組。首先,如果 \(s_i=1\) ,此時就會得到一個新的二元組 \((1,dp_{i-1})\) 。枚舉舊的二元組,假設枚舉到 \((l,v)\) ,討論長度爲 \(l\) 的斐波那契串前綴是否能拓展 \(s_i\) 這一位。利用第二條性質,我們可以得出一個長度爲 \(l+1\) 的斐波那契串前綴最後拼接的斐波那契串是否爲 \(1\) 。如果是的話,那麼 \(s_i\) 就必須爲 \(1\) ,否則必須爲 \(0\) 。如果可以拓展這一位,那麼檢查 \(l+1\) 是否爲斐波那契數,如果是,那麼就讓 \(dp_i\)-=\(v\)\(dp_i\) 初始爲 \(\sum_{j=1}^{i-1}dp_j\) )。那麼複雜度是小於 \(\text O(32\sum_{i=1}^n|s_i|)\) 的。

cint maxn = 1010, mod = 998244353;
int n, len, fib[33], l[33], f[33], nl[33], nf[33], tot, ans = 1, sum = 1;
char s[maxn];
map<int, int> vis;
il int getnxt(int len){
	fb(i, 32, 2)if(len > fib[i])len -= fib[i];
	return len == 1;
}
il void calc(int c){
	int nans = (sum-ans+mod)%mod, ntot = 0;
	if(c)ntot = 1, nl[ntot] = 1, nf[ntot] = ans;
	fp(i, 1, tot){
		if(c != getnxt(l[i]+1))continue;
		if(vis.count(l[i]+1))nans = (nans-f[i]+mod)%mod;
		nl[++ntot] = l[i]+1, nf[ntot] = f[i];
	}
	ans = nans, sum = (sum+ans)%mod, tot = ntot;
	fp(i, 1, tot)f[i] = nf[i], l[i] = nl[i];
}
int main(){
	fib[0] = fib[1] =1, vis[1] = 1;
	fp(i, 2, 32)fib[i] = fib[i-1]+fib[i-2], vis[fib[i]] = 1;
	scanf("%d", &n);
	while(n--){
		scanf("%s", s+1), len = strlen(s+1);
		fp(i, 1, len)calc(s[i]-'0');
		printf("%d\n", ans);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章