CF1950 A~G

Crazyouth發表於2024-05-03

前言

報了名沒打的一場 Div. 4,我是怎麼想到回去做的呢?上課的時候無聊於是隨機了一道 1700 的題,找到了本場比賽的 F 題,我那時還沒發現。過了差不多 \(2\sim3\) 天去隨機了一道 1900,又找到了 G 題,一看 G 題竟然只有 1900,意識到這是 Div. 4,就想著 AK 一場 Div. 4,結果發現 F 做過了,這場我還報名了?於是倒序開題並 AK 了。根據我的習慣,每次完成一套題,就要發一個 Overall Tutorial,於是有了本文。

\(\textsf{Overall}\)

CF1950A

難度:800

直接比較三數即可。

CF1950B

難度:800

把每 \(4\) 格看做一格,當 \(i+j\)\(2\) 的倍數的時候這個格子就是 #

CF1950C

難度:800

除了 \(0\) 點鐘特判,其他輸出減一取模加一即可。

CF1950D

難度:1100

先簡化題意:一個數 \(x\) 能否拆成若干只由 01 組成的數的乘積。注意到這種數不多,列舉出來,計算哪些 \(x\) 可以表示,\(O(1)\) 判斷答案。

CF1950E

難度:1500

顯然 \(x\)\(n\) 的因數,所以可以列舉 \(n\) 的因數,再每 \(x\) 個字元檢查一次。把串 \(S\) 分成 \(x\) 個串,也就是 \(S_1S_2\cdots S_x,S_{x+1}S_{x+2}\cdots S_{2x},\cdots\),把這些子串放進 set 裡去重。如果 set 內只有一個串或兩個滿足條件的串且它們其中一種不超過一個,那麼 \(x\) 就是符合條件的答案。

#include <bits/stdc++.h>
using namespace std;
set<string> st;
map<string,int> mp;
int check(string s,string t)
{
	int cnt=0;
	for(int i=0;i<s.size();i++) cnt+=(s[i]!=t[i]);
	if(cnt>1) return 0;
	return 1;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	int t;
	cin>>t;
	while(t--)
	{
		int n;
		cin>>n;
		string s;
		cin>>s;
		s=' '+s;
		for(int i=1;i<=n;i++)
		{
			if(n%i) continue;
			st.clear();
			mp.clear();
			for(int j=1;j<=n;j+=i)
			{
				string t=s.substr(j,i);
				st.insert(t);
				mp[t]++;
			}
			if(st.size()>2)
			{
				continue;
			}
			if(st.size()==1)
			{
				cout<<i<<'\n';
				break;
			}
			if((mp[*st.begin()]>1&&mp[*(++st.begin())]>1)||!check(*st.begin(),*(++st.begin())))
			{
				continue;
			}
			cout<<i<<'\n';
			break;
		}
	}
}

CF1950F

難度:1700

點數不變,那麼平均每層節點越多,深度越小。不難聯想到對於某一層來說,它的節點最大值與它上一層節點個數和剩餘節點數量有關。因此應儘可能地把子節點最多的那 \(a\) 個放到深度小的位置,把次多的 \(b\) 個放到僅比那 \(a\) 個稍深一點的位置,\(c\) 個葉子有多深放多深。

#include <bits/stdc++.h>
using namespace std;
vector<int> G[300010];
int dep[300010];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	int t;
	cin>>t;
	while(t--)
	{
		int a,b,c,now=1;
		cin>>a>>b>>c;
		for(int i=1;i<=a+b+c;i++) G[i].clear(),dep[i]=0;
		queue<int> q;
		q.push(1);
		while(q.size())
		{
			int u=q.front();
			q.pop(); 
			if(a)
			{
				a--;
				G[u].push_back(++now);
				q.push(now);
				dep[now]=dep[u]+1;
				G[u].push_back(++now);
				q.push(now);
				dep[now]=dep[u]+1;
			}
			else if(b)
			{
				b--;
				G[u].push_back(++now);
				q.push(now);
				dep[now]=dep[u]+1;
			}
			else c--;
		}
		if(a||b||c)
		{
			cout<<-1<<'\n';
			continue;
		}
		int ans=0;
		for(int i=1;i<=now;i++) ans=max(ans,dep[i]);
		cout<<ans<<'\n';
	}
}

CF1950G

觀察到 \(n\) 的範圍極小,本題應該是要 \(O(2^n\times n)\) 左右的複雜度,不難想到狀壓 dp。定義 \(dp_{i,j}=0/1\) 表示達到 \(i\) 這個狀態(一個二進位制數,第 \(i\) 首歌選那麼狀態的第 \(i\) 位為 \(1\),否則為 \(0\)\(0\le i<n\))並以第 \(j\) 首歌結尾是否可能。有了狀態,自然推出轉移即某一個狀態 \(i\) 中選擇一個未被選擇的歌並且能與第 \(j\) 首歌接上,那麼假設該歌曲編號為 \(k\),那麼 \(dp_{i+2^k,k}\leftarrow 1\)。初始值的設定也易得:對於每個 \(0\le x<n\)\(dp_{2^x,x}\leftarrow 1\)。答案自然就是滿足 \(dp_{i,j}=1\)\(n-\operatorname{popcount}(i)\) 的最小值。

這題有點卡常,需要注意一些事情,否則就會如圖。

要注意的幾點:

  • popcount 提前處理好,最好是在多測開始前就把 \(0\sim 2^{16}\) 的每個數的 popcount 處理好。

  • 關閉同步流或使用 scanf 讀入或開快讀。

  • 歌曲流派與作者先離散化,否則常數很大。

  • 記錄一個 \(vis_{i,j}\) 表示是否計算過 \(dp_{i,j}\),如果搜尋到狀態 \(i,j\)\(vis_{i,j}=1\) 就直接退出本次搜尋。

  • 多測清空。

#include <bits/stdc++.h>
using namespace std;
string g[20],w[20];
int g2[20],w2[20],cl[1<<17];
int dp[1<<17][20],n,cnt=0,vis[1<<17][20];
map<string,int> mp;
void dfs(int sta,int lst)
{
	if(vis[sta][lst]) return;
	vis[sta][lst]=1;
	dp[sta][lst]=1;
	for(int i=0;i<n;i++)
	{
		if((sta>>i&1)||(g2[i]!=g2[lst]&&w2[i]!=w2[lst]))
		{
			vis[sta+(1<<i)][i]=1;
			continue;
		}
		dfs(sta+(1<<i),i);
	}
}
int calc(int k)
{
	int ret=0;
	for(int i=0;i<16;i++) ret+=k>>i&1;
	return ret;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	int t;
	cin>>t;
	for(int i=0;i<(1<<16);i++) cl[i]=calc(i);
	while(t--)
	{
		mp.clear();
		int ans=0;
		cin>>n;
		for(int i=0;i<n;i++)
		{
			cin>>g[i]>>w[i];
			if(!mp[g[i]]) mp[g[i]]=++cnt;
			if(!mp[w[i]]) mp[w[i]]=++cnt;
			g2[i]=mp[g[i]];
			w2[i]=mp[w[i]];
		}
		for(int i=0;i<(1<<n);i++)
		for(int j=0;j<n;j++)
		dp[i][j]=vis[i][j]=0;
		for(int i=0;i<n;i++) dfs(1<<i,i);
		for(int i=0;i<(1<<n);i++)
		for(int j=0;j<n;j++)
		ans=max(ans,dp[i][j]*cl[i]);
		cout<<n-ans<<'\n';
	}
}

相關文章