CF339 题解

发布时间 2023-07-14 17:43:21作者: The_Last_Candy

CF339 题解

这套题虽然是div2,但是具有一定的价值,这套题作为典型的div2题目,全套5道题都几乎用暴力方法解决,但是为什么这样是对的?令人深思。

A

红题,把个位数提出来再排序就好了。

#include<bits/stdc++.h>
using namespace std;
const int N = 105;
char s[N];
int n,num[N],tot = 0;
int main()
{
	scanf("%s",s + 1);
	n = strlen(s + 1);
 	for(int i = 1;i <= n;i++)
 	{
 		if(s[i] == '+') continue;
 		num[++tot] = s[i] - '0';
	}
	sort(num + 1,num + tot + 1);
	for(int i = 1;i <= tot - 1;i++) cout<<num[i]<<"+";
	cout<<num[tot];
	return 0;
}

B

路径只有一种,记录当前位置,下一个目标如果大于当前位置,就走小半圈,如果小于当前位置,就绕过1号节点一次,把距离加起来就好了。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
long long tp = 0;
int a[N],n,m;
int main()
{
	cin>>n>>m;
	for(int i = 1;i <= m;i++) cin>>a[i];
	int np = 1;
	for(int i = 1;i <= m;i++) tp += (np <= a[i] ? a[i] - np : a[i] + n - np),np = a[i];
	cout<<tp;
	return 0;
}

C

比赛才刚刚开始!

这道题其实可以导出一个\(dp\)做法,我们发现右边和左边差距不会太大,所以假设\(dp_{i,k,t}\)为前\(i\)个砝码,左边减右边的绝对值为\(k\)(毕竟我们只在乎这个对吧),第\(i\)个砝码是\(t\),是否能够达到。转移看\(i\)的奇偶性再看放哪边即可。但是我写挂了。

正解还是很逆天,首先提取出哪些砝码可以用,然后暴力从第一个开始\(dfs\),合格的砝码就放一个,然后换边,如果放完\(m\)个就输出方案,结束程序。

看上去是一个\(10^{1000}\)的暴力对吧?但是这玩意CF上跑了15ms。感觉本题的难度主要在于分析时间...

#include<bits/stdc++.h>
using namespace std;
int a[11],tot = 0,m,q[1005];
inline void dfs(int x,int ls,int rs,int last)
{
	if(x == m + 1)
	{
		puts("YES");
		for(int i = 1;i <= m;i++) cout<<q[i]<<" ";
		exit(0);
	}
	for(int i = 1;i <= tot;i++)
	{
		if(a[i] == last) continue;
		if(x & 1)
		{
			if(ls + a[i] > rs) q[x] = a[i],dfs(x + 1,ls + a[i],rs,a[i]);
		}
		else
			if(rs + a[i] > ls) q[x] = a[i],dfs(x + 1,ls,rs + a[i],a[i]);
	}
}
int main()
{
	char s[11];
	scanf("%s",s + 1);
	for(int i = 1;i <= 10;i++) if(s[i] == '1') a[++tot] = i;
	scanf("%d",&m);
	dfs(1,0,0,0);
	puts("NO");
	return 0;
}

D

感觉比较奇葩...

这个题我们发现它是\(2^n\)个(不然没法做),而且在每一层都是两两\(O(1)\)合并的规律。

分层...合并...

线段树!

所以从底层开始记录线段树高度,直接单点修改维护即可。板子题,CF特有的CD题难度逆序对

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int t[N * 4],dep[N * 4],n,m;
inline void pushup(int pos)
{
	if(dep[pos] & 1) t[pos] = t[pos << 1] | t[pos << 1 | 1];
	else t[pos] = t[pos << 1] ^ t[pos << 1 | 1];
}
inline void build(int l,int r,int pos)
{
	if(l == r)
	{
		cin>>t[pos];
		dep[pos] = 0;
		return;
	}
	int mid = (l + r) >> 1;
	build(l,mid,pos << 1);
	build(mid + 1,r,pos << 1 | 1);
	dep[pos] = dep[pos << 1] + 1;
	pushup(pos);
}
inline void modify(int l,int r,int x,int k,int pos)
{
	if(l == r)
	{
		t[pos] = k;
		return;
	}
	int mid = (l + r) >> 1;
	if(x <= mid) modify(l,mid,x,k,pos << 1);
	else modify(mid + 1,r,x,k,pos << 1 | 1);
	pushup(pos);
}
int main()
{
	cin>>n>>m;
	n = 1 << n;
	dep[1] = 0;
	build(1,n,1);
	int x,y;
	for(int i = 1;i <= m;i++)
	{
		cin>>x>>y;
		modify(1,n,x,y,1);
		cout<<t[1]<<endl;
	}
	return 0;
}

E

三步指的是题目保证三步有解(谴责翻译)。

考虑将这个变成一个逆序过程,即将原排列翻成\(1 \to n\)。我们手模可以发现只有和前后不连续的数字才有可能成为翻转的端点,所以我们可以在每一步找出这些端点,枚举当前步该以哪两个为端点翻转,\(dfs\)即可,每一步翻转最多制造4个这样的点,加上一头一尾,总共的情况数就大约有\(14^6 = 7529536\)个,可以通过此题。

#include<bits/stdc++.h>
using namespace std;
const int N = 1005;
pair<int,int> q[4];
int a[N],n;
inline bool sorted()
{
	for(int i = 1;i <= n;i++) if(a[i] != i) return 0;
	return 1;
}
inline void solve(int x)
{
	if(sorted())
	{
		cout<<x - 1<<endl;
		for(int i = x - 1;i >= 1;i--)
			cout<<q[i].first<<" "<<q[i].second<<endl;
		exit(0);
	}
	if(x >= 4) return;
	int now[15],top = 0;
	now[++top] = 1;
	now[++top] = n;
	for(int i = 2;i <= n;i++)
		if(abs(a[i] - a[i - 1]) > 1)
			now[++top] = i - 1,now[++top] = i;
	sort(now + 1,now + top + 1);
	top = unique(now + 1,now + top + 1) - (now + 1);
	for(int i = 1;i <= top;i++)
		for(int j = i + 1;j <= top;j++)
		{
			q[x] = make_pair(now[i],now[j]);
			reverse(a + now[i],a + now[j] + 1);
			solve(x + 1);
			reverse(a + now[i],a + now[j] + 1);
		}
}
int main()
{
	cin>>n;
	for(int i = 1;i <= n;i++)
		cin>>a[i];
	solve(1);
	return 0;
}