首页 > 其他分享 >CF1950 A~G

CF1950 A~G

时间:2024-05-03 12:11:38浏览次数:14  
标签:CF1950 dep int cin ++ mp now

前言

报了名没打的一场 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';
	}
}

标签:CF1950,dep,int,cin,++,mp,now
From: https://www.cnblogs.com/Crazyouth/p/18171087

相关文章

  • CF1950B Upscaling题解
    CF1950BUpscaling题解题意给予你一个正整数\(n\),构造一个如图的字符矩阵。思路注意数据\(1\len\le20\),可以发现数据很小,于是我们可以暴力模拟。我们可以将两列看为一列,两行看为一行。然而我们可以发现缩成的一行的\(i\)等于被缩的两行的\({i\div2}\)的向上取整。......