Codeforces Round #602

E. Not Same (2s 256Mb)

題目大意

n(1e3)個ai,表示第i處有ai個塊。每次可以選擇下標的一個子集,使子集內的塊減1,不能減塊數爲0的位置。要求選擇n+1個子集至多,且使用子集兩兩不同。輸出任意一個構造答案。

樣例輸入輸出

分析(to be continue)

一個2500的題在賽後我看的時候被我數十分鐘一己之力幹掉,一發入魂,還和題解方法不一樣,還是挺吊的,雖然我都不太明白我的做法爲什麼對。。。

通過一番在草稿紙上發揮,我覺得應該對每個ai構造答案每一列。不知道怎麼,我就得出按降序排列ai去求解。。。然後我就奇思妙想到

代碼

int main()
{
	n = read();
	for(int i = 1; i <= n; i++)
	{
		a[i] = {i, read()};
	}
	sort(a+1, a+n+1);
	for(int i = 1; i <= n; i++)
	{
		int pos = a[i].id, j = i, sum = a[i].v;
		while(sum--)
		{
			ans[j][pos] = 1;
			j = j%(n+1)+1;
		}
	}
	printf("%d\n", n+1);
	for(int i = 1; i <= n+1; i++)
	{
		for(int j = 1; j <= n; j++) putchar(ans[i][j]+'0');
		putchar(10);
	}
	return 0;
}

D. Wrong Answer on test 233 (Easy/Hard Version) (1s 256Mb)

題目大意

n(2000/2e5)個選擇題,每個都有k種選項。給出hi爲第i個題的答案,求有多少種答題方案,使得此方案向右移動一位(n->1)去答題比原情況得分嚴格高。mod 998244353。

分析

Easy:n2dp表示種數,一維前i位,一維得分差,n*n轉移。

Hard:設移動後得分減移動前得分爲d,每個位置選擇答案爲ai。對於每個位置,如果h_i = h_{i+1},則無論如何設置,對d沒有影響。否則可以使a_i = h_i使得d加1,也可以使a_i = h_{i+1}使得d減1,其他設置ai對d無影響。設可有影響的題數爲t。枚舉t裏有i題對答案無影響,則答案爲k^{n-t} \times \sum_{0 \leq i \leq t - 1}{[(k-2)^i \times \binom{t}{i} \times \sum_{[\frac{t-i}{2}]+1 \leq j \leq t - i}{\binom{t-i}{j}}]}。不能影響題隨便選擇,可有但無從k-2種裏面選擇,剩餘組合數學。

強大的wzgy的代碼教會我們,每一種d>0的情況,都一一對應一種d<0的情況,所以只需將所有情況計算出來,減去平局d=0,再除以2即可,wow!

代碼

int main()
{
	n = read(), k = read();
	pre();
	for(int i = 1; i <= n; i++) a[i] = read();
	if(k == 1) //特判k=1時
	{
		printf("0\n");
		return 0;
	}
	for(int i = 1; i <= n; i++) if(a[i] != a[i%n+1]) ab++;//計算可改變d的題目數量
	int ans = 0;
	for(int i = 0; i*2 <= ab; i++) //求解平局情況
	{
		ans = (ans + 1LL*C(ab, i*2)*C(i*2, i)%mod*km(k-2, ab-i*2)%mod)%mod;
	}
	ans = 1LL*(km(k, ab)-ans+mod)*inv[2]%mod; //所以情況減去平局除以2
	ans = 1LL*ans*km(k, n-ab)%mod; //最後乘上不可改變d的題的每題k個隨便選
	printf("%d\n", ans);
	return 0;
}

C. Arson In Berland Forest (2s 512Mb)

題目大意

n*m '.' 和 'X' 圖 (1 \leq n,m \leq 1e6, 1 \leq n\cdot m \leq 1e6),'X'表示已被火燒的樹,'.'則表示未被燒。燃燒情況是每棵燃燒的樹通過1秒將八個方向的樹燒着。構造一個可行答案,要求經過時間T最大,並輸出第0時刻已經燒着的樹是哪些,這些樹的數量沒有限制。

樣例輸入輸出

分析

顯然二分時間T,關鍵在於check。從下到上,從右到左,求解右下方可燃燒範圍。如果>=2*T+1,可以在右+T下+T放初始燃燒樹。最後判斷給個每棵樹是不是都被放置初始燃燒樹燃燒。判斷時,可以用一個巧方法,將燃燒等價於從左上角往右下燃燒2*T+1,所以判斷時可以看左上三個方向那個剩餘燃燒範圍最大。

題解說也可以用bfs,判斷某個點是否最近的'.'的距離大於T,使用一開始一遍bfs。不是太懂,懵比。

代碼

int ma[maxn], vis[maxn];
int check(int x)
{
	memset(vis, 0, sizeof(vis));
	int s = x<<1|1;
	for(int i = 1; i <= n; i++) //放置初始燃燒樹
	for(int j = 1; j <= m; j++) if(ma[cor(i, j)] >= s) vis[cor(i, j)] = s;
	
	for(int i = 1; i <= n; i++) //將所有可燒的樹燒掉
	for(int j = 1; j <= m; j++) 
		vis[cor(i, j)] = max(vis[cor(i, j)], -1+max(vis[cor(i-1, j-1)], max(vis[cor(i-1, j)], vis[cor(i, j-1)])));

	for(int i = 1; i <= n; i++) //判斷是否所有的樹都被燒到
	for(int j = 1; j <= m; j++) if(a[cor(i, j)] && vis[cor(i, j)] <= 0) return 0;
	return 1;
}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i++)
	{
		getchar();
		for(int j = 1; j <= m; j++) a[cor(i, j)] = getchar() == 'X';
	}
	for(int i = n; i; i--) //求解最遠燃燒距離
	for(int j = m; j; j--)
	{
		if(a[cor(i, j)])
		{
			ma[cor(i, j)] = 1 + min(ma[cor(i+1, j+1)], min(ma[cor(i+1, j)], ma[cor(i, j+1)]));
		}
	}
	int l = 0, r = 1e6; // r可以是min(n, m)
	while(l != r)
	{
		int mid = l + ((r-l) >> 1) + 1;
		if(check(mid)) l = mid;
		else r = mid-1;
	}
	int ans = l;
	printf("%d\n", ans);
	for(int i = 1; i <= n; i++) //構造答案
	for(int j = 1; j <= m; j++) if(ma[cor(i, j)] >= ans*2+1) lis[cor(i+ans, j+ans)] = 1;
	for(int i = 1; i <= n; i++)
	{
		for(int j = 1; j <= m; j++) putchar(lis[cor(i, j)] ? 'X' : '.');
		putchar(10);
	}
	return 0;
}

B. Optimal Subsequences (Easy/Hard Version) (3s 256Mb)

題目大意

n(100/2e5)個ai(1e9)有序,m(100/2e5)個詢問,每個詢問有k(1-n)和pos(1-k),求將前k大的ai按原序排列(即subsequence)(如果相同,取字典序最小),問此序列中第pos個元素是多少。

分析

Easy:暴力構造所有子序列即可。

Hard:線段樹。離線按k排序詢問。將ai先按大小排序,再按下標排序(字典序,小的靠前)。處理每個k詢問,將前k大ai下標加入線段樹,求位置爲pos的元素。

代碼

int n, m, b[maxn], t[maxn<<2];
void add(int k, int l, int r, int pos)
{
	if(pos < l || r < pos) return;
	t[k]++;
	if(l == r) return;
	add(lson, l, mid, pos); add(rson, mid+1, r, pos); return;
}

int ask(int k, int l, int r, int y)
{
	if(l == r) return l;
	if(t[lson] >= y) return ask(lson, l, mid, y);
	else return ask(rson, mid+1, r, y-t[lson]);
}

int cmp(Query aa, Query bb) { return aa.id < bb.id; }

int main()
{
	n = read();
	for(int i = 1; i <= n; i++) b[i] = a[i].v = read(), a[i].id = i;
	sort(a+1, a+n+1);
	m = read();
	for(int i = 1; i <= m; i++) q[i].k = read(), q[i].pos = read(), q[i].id = i;
	sort(q+1, q+m+1);
	int tmp = 0;
	for(int i = 1; i <= m; i++)
	{
		while(q[i].k > tmp) add(1, 1, n, a[++tmp].id);
		q[i].ans = ask(1, 1, n, q[i].pos);
	}
	sort(q+1, q+m+1, cmp);
	for(int i = 1; i <= m; i++) printf("%d\n", b[q[i].ans]);
	return 0;
}

 

B. Box (1s 256Mb)

題目大意

給出 n(1e5),n個qi,表示一個排列前i個元素裏的最大值,求構造任意一個合法排列,沒有輸出-1。

分析

如果qi>qi+1,那麼pi+1 = qi+1;否則,使用盡量小的未使用元素去填,沒有可填的則非法。

代碼

int main()
{
	int T = read();
	while(T--)
	{
		n = read();
		int mx = 0, pos = 1, flag = 1;
		for(int i = 1; i <= n; i++) a[i] = read();
		for(int i = 1; i <= n; i++)
		{
			if(a[i] < mx) 
			{
				flag = 0;
				break;
			}
			else if(a[i] > mx)
			{
				if(vis[a[i]]) { flag = 0; break; }
				mx = ans[i] = a[i];
				vis[a[i]] = 1;
			}
			else
			{
				while(pos <= n && vis[pos]) pos++;
				if(pos > mx) { flag = 0; break; }
				vis[ans[i] = pos] = 1;
			}
		}
		if(!flag) printf("-1\n");
		else 
		{
			for(int i = 1; i < n; i++) printf("%d ", ans[i]);
			printf("%d\n", ans[n]);
		}
		memset(vis, 0, sizeof(int)*(n+3));
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章