ATCoder code festival 2016 qual C

[AtCoder] code festival 2016 qual C

說在前面

在洛谷水題看到了\(A\)題, 而後就花了一個晚自習把這個比賽作了\(2333\)
原題目連接:
AtCoder
在洛谷題號爲AT2084 ~ AT2088
這篇博客前兩題是洛谷的翻譯,後三道本身翻譯的可能有鍋\(2333\)
已在洛谷提交翻譯等待審覈html

官方題解連接:
Editorialc++

作題借鑑了兩位神仙的題解:
http://www.javashuo.com/article/p-clvnluag-q.html
http://www.javashuo.com/article/p-zmwnnpiq-hy.html數組

A CF 終究是ATCoder變了心

問一個字符串S(2<=S<=100)中是否能夠去掉幾個字符,變成CF(大寫)兩個字母,若是能夠,輸出‘Yes’,不能夠,輸出‘No’。post

這個題大水 直接放代碼spa

#include<bits/stdc++.h>
using namespace std;
char s[200];
int main()
{
	cin >> s + 1;
	int len = strlen(s + 1);
	bool c,f;
	c = f = 0;
	for(register int i = 1; i <= len; ++i)
	{
		if(s[i] == 'C')
			c = 1;
		if(s[i] == 'F' && c)
			f = 1;
	}
	puts(c && f ? "Yes" : "No");
	return 0;
}

B

某人有K塊蛋糕,他想天天吃一塊,正好K天吃完。
這K塊蛋糕共分爲T種,第i種蛋糕有a[i]個。
這我的不想連續兩天吃一樣種類的蛋糕,他想知道本身最少有多少天本身吃的蛋糕和前一天吃的蛋糕種類同樣。翻譯

咱們考慮如何才能儘量的讓他不吃到一樣的蛋糕。必定是相同蛋糕之間有其餘蛋糕隔開。所以出現次數最多的蛋糕必定能夠用來分割其餘的蛋糕。而若是有蛋糕相鄰,也必定是出現次數最多的蛋糕已經將其他蛋糕徹底分割依舊有剩餘。
設最多的蛋糕有n塊。
答案就是:\(max(n - 1 - (k - n), 0)\)
注:減一是由於第一天並不計算。code

//代碼的變量名和題目不同
#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n, T;
	cin >> n >> T;
	int mx = 0;
	for(register int i = 1; i <= T; ++i)
	{
		int x;
		cin >> x;
		mx = max(mx, x);
	}
	cout << max(mx - 1 - (n - mx), 0) << endl;
}

C 二人のアルピニスト

高橋君和青木君去徒步爬過了一個著名的山脈。這個山脈由不少個山東西走向依次排開,高橋君從東往西爬,青木君從西往東爬。他們只能記住本身到目前爲止通過的最高的山是多高。高橋君記錄下的是\(T_i\),青木君記下的是\(A_i\)
詢問山高的可能序列的個數,答案對\(1e9 + 7\)取模。
若是給出的序列不合法輸出\(0\)
一句話題意:告訴你一個序列前綴最大值和後綴最大值,求這個序列的可能個數。
範圍:
\(1 \leq N \leq 1e5\)
$1 \leq A_i \leq 1e9 $
$1 \leq T_i \leq 1e9 $
保證T序列單調不降, A序列單調不增。(都是從左往右)htm

很顯然咱們能夠發現一個性質,若是\(T_i\)\(T_{i+1}\)不一樣,第\(i + 1\)個山的高度是已知的(\(A\)同理)。因而咱們能夠推斷出全部已知的山高,而其他未知的地方,山的高度\(h_i\)多是\(\forall h_i \in [1, \min(T_i,A_i)]\),答案直接乘上就能夠了。blog

const int MAXN = 1e5 + 10;
const int MOD = 1e9 + 7;
int n;
int T[MAXN], A[MAXN];
int  h[MAXN];
signed main()
{
	poread(n);
	for(register int i = 1; i <= n; ++i)
		poread(T[i]);
	for(register int i = 1; i <= n; ++i)
		poread(A[i]);
	for(register int i = 1; i <= n; ++i)
		if(T[i] != T[i - 1])
			h[i] = T[i];
	for(register int i = n; i >= 1; --i)
		if(A[i] != A[i + 1])
			h[i] = A[i];
	for(register int i = 1, mx = 0; i <= n; ++i)
	{
		mx = max(h[i], mx);
		if(mx != T[i])
		{
			puts("0");
			return 0;
		}
	}
	for(register int i = n, mx = 0; i >= 1; --i)
	{
		mx = max(h[i], mx);
		if(mx != A[i])
		{
			puts("0");
			return 0;
		}
	}
	int ans = 1;
	for(register int i = 1; i <= n; ++i)
	{
		if(!h[i])
		{
			ans = (long long) ans * min(A[i],T[i]) % MOD;
		}
	}
	cerr << ans << endl;
	cout << ans << endl;
}

D Friction

高橋君有一個\(h * w\)的藝術品,藝術品的每一塊都有一個小寫字母表示顏色。
高橋君想把這個藝術品拆掉,他採起這樣的方式:
選取一列並把這一列向下推一格,這樣這一列最下邊的顏色會消失。
可是這會產生代價。產生的代價是選擇的這一列中,與相鄰格子顏色相同的格子數。確切來講,當有一對格子\((p,q)\)知足ci

  • \(p\)在所選的這一列
  • \(p,q\)相鄰
  • \(p,q\)顏色相同

這對格子會產生\(1\)的代價。
請計算高橋君把這個藝術品徹底拆除所須要的最小代價。
範圍:
\(1 \leq h \leq 300\)
\(2 \leq w \leq 300\)

咱們發現,相鄰兩列的代價與其餘兩列無關,好比一、2列的代價並不會影響二、3列的代價。所以\(DP\)計算相鄰兩列的計劃。
\(f[x][y]\)表示第一列往下推了\(x\)格, 第二列往下推了\(y\)格的最小代價。
轉移顯然 \(f[x][y] = \min(f[x-1][y] + cost[x-1][y],f[x][y-1] + cost[x][y-1] )\)
\(cost\)可使用前綴和計算,節省一維的時間複雜度。
每次計算相鄰兩列便可。時間複雜度\(O(h^2 \cdot w)\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN =  305;
int n, m;
char th[MAXN][MAXN];
inline int calc(const int &x)
{
	static int f[MAXN][MAXN];
	for(register int i = 1; i <= n; ++i)
		for(register int j = 1; j <= n; ++j)
			f[i][j] = (th[i][x] == th[j][x + 1]);
	for(register int i = 1; i <= n; ++i)
		for(register int j = 1; j <= n; ++j)
			f[i][j] = f[i - 1][j - 1] + f[i][j];
	for(register int i = 1; i <= n; ++i)
		for(register int j = 1; j <= n; ++j)
			f[i][j] += min(f[i - 1][j], f[i][j - 1]);
	return f[n][n];
}
int main()
{
	scanf("%d %d", &n, &m);
	for(register int i = 1; i <= n; ++i)
		scanf("%s", th[i] + 1);	
	register int ans = 0;
	for(register int i = 1; i < m; ++i)
		ans += calc(i);
	cout << ans << endl;
}

至於相鄰兩列代價與其餘列無關的問題,我一開始也有疑問,而網上找到的題解都沒有詳細解釋。我本身嘗試證實感性理解了一波。可能出鍋。
咱們首先考慮只有一、2兩列的狀況,這兩列之間會有一個最優解。再考慮加進來第3列,二、3列之間也會有一個最優解。這兩個最優解爲什麼不會衝突呢?
由於他們的關係只是這兩列之間的相對關係,就拿第三個樣例的前三列來講。
一、2列的最優解應該是:第一列向下兩次,第二列向下一次,以後每列輪換向下移動,最小代價爲4。
不畫圖了表格寫一會兒,本身手玩。

1 2 3 4
aa a a
ab ab b a
ab ab ab ab
ab ab ab ab
aa aa aa ab

而二、3列之間應該是將第3列一直推到底,總代價爲8。

1 2 3 4 5 6
aa a a a a a
bb ba b b b b
ba bb ba b b b
bb ba bb ba b b
aa ab aa ab aa a

這兩列合到一塊兒呢?
實際能夠先操做第3列,再操做一、2,並不會對結果產生影響。
以此類推,第\(i\)列與第\(i + 1\)列的操做都是相對的,所以相鄰並無影響,\(DP\)結果直接相加便可。

E 順列辭書

對於一個長爲\(n\)的序列,它的全排列有\(n!\)種。
高橋君有一本記錄着長爲\(n\)的序列的全排列、有\(n!\)頁的序列詞典,詞典的第\(i\)頁記錄着排名爲\(i\)的排列。
高橋君想要查找一個序列,但他忘了其中的幾個數。因此他要查詢全部可能記錄這個序列的頁數。
如今告訴你他還記得的部分序列,他忘記的地方是\(0\),請你告訴他,他全部要查詢的頁碼之和,答案對\(1e9 + 7\)取模。
一句話題意:給一個不全的排列,求出全部可能的排列的排名之和。
範圍:\(1 \leq n \leq 5e5\)

這道題上網找題解可是題解解釋都不怎麼詳細,本身看了半天才懂。\(2333\)
如下解釋可能出鍋。

真的鍋了
感謝@nofind@jiqimao神仙指出
已修正

前置知識:康託展開
推薦nofind神仙的博客

設該排列的第\(i\)項是\(a_i\)
先考慮康託展開。
\(\sum\limits_{i = 1}^{n} A_i \cdot (n - i)!\)
其中 \(A_i =a_i - \sum\limits_{j = 1}^{i}[a_j \leq a_i]\)
\((n - i)\)能夠很快計算,咱們先考慮如何計算全部可能排列的\(A_i\)之和。
\(m\)是未知數的個數。易知可能的排列有\(m!\)種。
分狀況討論:\(a_i\)已知和\(a_i\)未知。

  • \(a_i\)已知時,若\(a_j\)已知,咱們能夠利用樹狀數組快速計算\(A_i\),而且在全部的可能排列中,這個貢獻不變。所以這一項的貢獻就是
    \(\sum\limits_{j=1}^{i}[a_j < a_i] * m!\)
    同時計算第\(i\)位前面和後面未知的數與\(a_i\)所能產生的貢獻。
    \(i\)位後面的數\(a_k\)產生貢獻當且僅當\(a_k\)小於\(a_i\),所以所產生的貢獻就是任選一個大於\(a_i\)的未知數填在後面的任意一位,其他數任意排列。
    \(\sum\limits_{k = 1}^{n}[a_k > a_i \&\&a_k\)未知\(] * (m - 1)!\) \(*\) 後邊未知的位置數。
    \(i\)位前面的數\(a_j\)產生貢獻分析同上。
    \(\sum\limits_{j=1}^{n}[a_j < a_i \&\& a_j\)未知\(] * (m - 1)!\) \(*\) 前面未知的位置數。
  • \(a_i\)未知時,\(a_j\)已知的狀況已經包含在上面,考慮\(a_j\)未知的狀況。
    此時的方案即是從\(m\)個未知數中任意挑選兩個、其餘的位置數任填的方案,而能產生的貢獻的位置即是所剩下的未填數的位置。
    \(cnt\)表示已經計算貢獻的未知位置數。
    貢獻爲\(C_{m}^{2} * (m - 2)! * (m - cnt)\)

以上式子,沒有計算\((n - i)!\),程序裏算上了有些不一樣。
因爲是排名的和,記得最後加上\(m!\)
上邊式子中要預處理一些東西,這個影響就不大了看代碼吧。

signed main()
{
	poread(n);
	fac[0] = 1;
	for(register int i = 1; i <= n; ++i)
		fac[i] = (long long)fac[i - 1] * i % MOD;
	int m = n;
	for(register int i = 1; i <= n; ++i)
	{
		poread(a[i]);
		if(a[i])
			++s[a[i]], --m;
	}
	for(register int i = 1; i <= n; ++i)
		suf[i] = s[i] = 1 - s[i], s[i] += s[i - 1];
	for(register int i = n; i >= 1; --i)
		suf[i] += suf[i + 1];
	register int sum = 0, cnt = 0;
	register int res = 0;
	for(register int i = n; i >= 1; --i)
	{
		if(a[i])
		{
			sum = (long long)ask(a[i] - 1) * fac[m] % MOD;
			if(cnt)
				sum = (sum + (long long)cnt * s[a[i]] % MOD * fac[m - 1] % MOD) % MOD;
			res = (res + (long long)sum * fac[n - i] % MOD) % MOD;
			add(a[i], 1);
		}
		else
		{
			++cnt;
		}
	}
	cnt = sum = 0;
	for(register int i = 1; i <= n; ++i)
	{
		if(a[i])
		{
			if(cnt)
				res = (res + (long long)sum * suf[a[i]] % MOD * fac[m - 1] % MOD) % MOD;
		}
		else
		{
			sum = (sum + fac[n - i]) % MOD;
			++cnt;
		}
	}
	sum =  (long long)(m * (m - 1) >> 1 ) % MOD, cnt = 0;
	if(m >= 2)	
		for(register int i = 1; i <= n; ++i)
		{
			if(!a[i])
			{
				++cnt;
				res = (res + (long long)sum * (m - cnt) % MOD * fac[m - 2] % MOD * fac[n - i] % MOD);
			}
		}
	res = (res + fac[m]) % MOD;
	cout << res << endl;
}
相關文章
相關標籤/搜索