CF1492

A.Three Swimmers

原题链接CF

题目大意:

给出T组数据,每组数据给出p,a,b,c,求最小的是a,b或c的倍数且大于等于p的数与p的差值。

解题思路:

直接做做完了。每次使p分别除以a,b,c(向上取整),取最小值再算差值即可。

小代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
int T;
int a,b,c,p;

signed main()
{
	scanf("%lld",&T);
	while (T--)
	{
		scanf("%lld%lld%lld%lld",&p,&a,&b,&c);
		int ans=0x3f3f3f3f3f3f3f3f;
		ans=min(ans,(p+a-1)/a*a);
		ans=min(ans,(p+b-1)/b*b);
		ans=min(ans,(p+c-1)/c*c);
		printf("%lld\n",ans-p);
	}
	return 0;
}

B.Card Deck

原题链接

题目大意:

给出T组数据,每组数据给出栈中的\(n(1\leqslant n\leqslant 1e5)\)个元素\(p_{i}\),每次操作可以取出栈顶若干元素,按原来顺序放入新的栈中,求最后使得新栈值最大的方案。我们定义栈的值为\(\sum_{i=1}^{n} n^{n-i} p_{i}\)(默认栈顶为元素\(p_{n}\),栈底为元素\(p_{1}\))

解题思路:

这题简直就是反过来的出栈序列

容易想到贪心。越大的值越早到新栈里,新栈的值一定会越大。于是乎,我们定义一个前缀最大值数组,从后往前扫描,若出现旧的最大值了就输出当前的栈中所有元素(可结合代码理解)。

小?代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int T;
int n;
int p[N];
stack <int> s;
int mx[N];

signed main()
{
	scanf("%lld",&T);
	while (T--)
	{
		scanf("%lld",&n);
		for (int i=1;i<=n;i++) 
		{
			scanf("%lld",&p[i]);
			mx[i]=max(mx[i-1],p[i]);//前缀最大值
		}
		for (int i=n;i>=1;i--)
		{
			s.push(i);
			while (!s.empty()&&mx[s.top()]>mx[i-1])//目前栈中存放的有好用的最大值
			{
				printf("%lld ",p[s.top()]);
				s.pop();
			}
		}
		printf("\n");
	}
	return 0;
}

C.Maximum width

原题链接

题目大意:

给出两个长度分别为n,m的字符串\(s1,s2(2\leqslant m\leqslant n\leqslant2e5)\),保证\(s2\)\(s1\)的一个子串。要求找到\(s1\)的一个与\(s2\)相同的子串,使得该子串中相邻两个字符在\(s1\)中的距离最大,求这个最大值。

解题思路:

想让相邻两个字符距离最大,那么就是让相邻两个字符在可取到的范围里一个取最前面、一个取最后面,这样的距离最大。

于是乎,预处理出\(s2\)中每个字符可取到的最前、最后的位置,最后取最大的\(lst_{i+1}-fst_{i}\)就是答案。

小代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int n,m;
string s1,s2;
int fst[N],lst[N];
int ans;

signed main()
{
	cin>>n>>m>>s1>>s2;
	s1=" "+s1,s2=" "+s2; 
	int j=0;
	for (int i=1;i<=m;i++)
	{
		while (s1[++j]!=s2[i]);
		fst[i]=j;
	}
	j=n+1;
	for (int i=m;i>=1;i--)
	{
		while (s1[--j]!=s2[i]);
		lst[i]=j;
	}
	for (int i=1;i<m;i++) ans=max(ans,lst[i+1]-fst[i]);
	cout<<ans;
	return 0;
}

D.Genius's Gambit

原题链接CF

题目大意:

给出三个整数\(a,b,k(0\leqslant a,1\leqslant b,k\leqslant a+b\leqslant 2e5)\),要求构造出两个二进制形式的整数(x)2,(y)2,使得x,y都由a个"0",b个"1"组成,且\((x-y)_{2}\)有k个"1",输出x,y的构造方案,若无法构造则输出"No"(不允许前导0的存在)

解题思路:

我们不妨构造出最大的x,然后考虑\((x-y)_{2}\)有k个"1"的情况。当x,y相同数位的数不同时,x-y该数位就是1;若此次减法还向上借位,那么一直到上一个\((x)_{2}\)"1"、\((y)_{2}\)"0"的位置借位才会结束,在这个范围内x-y数位上都是1。

于是乎,我们可以在x上进行修改,找到l,r使得\(r-l=k\)并且\(x_{l}=1,x_{r}=0\),将\(x_{l}\)改为0、\(x_{r}\)改为1,这样修改过后的就是我们需要的y了。

挂分小技巧:漏掉无法构造成功的三种情况——a,b,k的边界问题。

不好看的代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int a,b,k;
int a1[N*2],a2[N*2];
int l,r;

signed main()
{
	cin>>a>>b>>k;
	if ((k+2>a+b&&k!=0)||(b==1&&k!=0)||(a==0&&k!=0))
	{
		cout<<"No";
		return 0;
	}
	l=2,r=l+k;
	for (int i=1;i<=b;i++) a1[i]=a2[i]=1;
	for (int i=b+1;i<=a+b;i++) a1[i]=a2[i]=0;
	if (k!=0)
	{
		while (r<=b) l++,r++;
		a2[l]=0,a2[r]=1;
	}
	cout<<"Yes"<<endl;
	for (int i=1;i<=a+b;i++) cout<<a1[i];
	cout<<endl;
	for (int i=1;i<=a+b;i++) cout<<a2[i];
	return 0;
}

E.Almost Fault-Tolerant Database

原题链接CF

题目大意:

给出n个长度为m的序列a,需构造一个序列s,使得对于任意序列,\(\sum_{i=1}^{m} [a_{i}\neq s_{i}]\leqslant2\)。输出构造方案,若无法构造则输出"No"。(\(nm\leqslant2.5\times10^{5}\))

解题思路(口胡版):

我们不妨令\(s=a_{1}\),跑一遍,记录每个序列与s的最大差值\(\Delta\),若\(\Delta\leqslant2\),那么直接输出s就行;若\(\Delta>4\),那么一定没有构造方案(可感性理解一下)。

那么现在就是\(\Delta=3\)\(\Delta=4\)的情况了。记差值与\(a_{1}\)最大的序列为\(a_{id}\)

\(\Delta=3\),考虑\(S=\{ans_{x},ans_{y},ans_{z}\}\),那么\(S\)中必然有一个元素与\(a_{1}\)中的相同,有另一个元素与\(a_{id}\)中的相同(这是显然的)。先令\(ans_{x}=a_{1,x}\)\(ans_{y}=a_{id,y}\),随后再使\(ans_{z}=a_{1,z}\),向上述一样再次跑一遍,若\(a_{id'}\)\(ans\)有三个不同,那么令\(ans_{z}=a_{id',z}\)再检查第二遍即可。若\(\Delta'>2\),那么枚举下一种方案。共有6种方案,每种方案最多检查2遍,复杂度最多为 \(O(12nm)\)

\(\Delta=4\),这是好考虑的,\(S\)中必然有两个元素分别与\(a_{1}、a_{id}\)中的元素相同。共有6种方案,枚举即可,复杂度最多为 \(O(6nm)\)

口胡完毕


posted @ 2024-11-08 14:44  还是沄沄沄  阅读(101)  评论(0编辑  收藏  举报