CF1834 题解

CF1834 题解

A

考虑答案与元素位置无关,只与11的个数有关。要求1必须多于或等于1,并且1个数为偶数。分讨:

序列中num(1)num(1),只需要看num(1)正负性,奇数1步,偶数0步

序列中num(1)<num(1),先通过11将数目补到第一种情况,再做第一种情况即可。

时间复杂度O(n)

#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int a[N],n,s[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n;
		memset(a,0,sizeof(a));
		memset(s,0,sizeof(s));
		for(int i = 1;i <= n;i++) cin>>a[i],s[i] = s[i - 1] + a[i];
		if(s[n] >= 0) cout<<((n - s[n]) % 4 == 0 ? 0 : 1)<<endl;
		else cout<<((n / 2) % 2 == 0 ? (-s[n] + 1) / 2 : (-s[n] + 1) / 2 + 1)<<endl;
	}
	return 0;
}

B

考虑90对应一定是最大的,我们尽量凑成90配对的情况。将LR用前导0补齐,我们不难发现在前面若干个相同的数位是无法产生贡献的,直到第一个不同的数位时,将L往上取,这一位后面全部赋为9,R往下取,这一位后面全部赋为0,这样就保证这一位的贡献没有下降,后面的贡献都取到了最大值,从而是最优解。

时间复杂度O(n)

#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int n,m;
char a[N],b[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		scanf("%s",a + 1);
		scanf("%s",b + 1);
		n = strlen(a + 1);
		m = strlen(b + 1);
		if(n < m) 
		{
			for(int i = n;i >= 1;i--) a[i + m - n] = a[i];	
			for(int i = 1;i <= m - n;i++) a[i] = '0';
		}
		int ans = 0;
		for(int i = 1;i <= m;i++)
			if(a[i] != b[i])
			{
				ans += (m - i) * 9 + abs(b[i] - a[i]);
				break;
			}
		cout<<ans<<endl;
	}
	return 0;
}

C

既然Alice想要将步数最小化,那么她就要尽量将两个字符串之间的差异消掉。我们发现S,T的对应方式只有两种(s1t1,sntn  s1tn,snt1)。所以Bob翻转哪个串都只是改变了一次对应方式,如果Alice一开始就按照一种对应方式消除差异的话,Bob完全干扰不了Alice,所以得出结论:Bob是小丑,最多只能将步数拖1步,选择权在于Alice。

宁愿当小丑也在所不惜,全力以赴地进行着必输的游戏......仅仅是他深爱着她。

上文说了,只有两种对应方式,所以Alice自然选的就是步数小的那个。我们发现由于Alice改变一次,Bob就要翻转一次,所以步数等于对应方式的差异数乘2,根据差异数的奇偶性和对应顺序决定是否需要-1(Bob最后一次翻之前就对应上了)

时间复杂度O(n)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
char s[N],t[N];
int n;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n;
		scanf("%s",s + 1);
		scanf("%s",t + 1);
		int dif = 0,rdif = 0,step = 0,rstep = 0;
		for(int i = 1;i <= n;i++) if(s[i] != t[i]) ++dif;
		for(int i = 1;i <= n;i++) if(s[i] != t[n + 1 - i]) ++rdif;
		step = dif << 1;
		if(dif & 1) step--;
		if(dif == 0) step = 0;
		rstep = rdif << 1;
		if(!(rdif & 1)) rstep--;
		if(rdif == 0) rstep = 2;
		cout<<min(step,rstep)<<endl;
	}
	return 0;
}

D

神犇jeefy有一个O(n)的做法,详见https://www.cnblogs.com/jeefy/p/17509939.html

考虑到对于一个人,如果他是极差中的最大值,那么所选的点就是他学过的所有点(对于学过的,多选一个他的高度就+1,最小值的高度可能+1,一定不劣;对于他没学过的,选了以后他的高度不变,最小值的高度可能+1,一定不优)。

所以我们可以枚举这个最大值是谁,可能的最小值与这个人可能有3种关系:不交,相交,最小值包含于这个人。先不考虑包含,这一部分很简单,我们尽量选一个人让他们不交,所以记录全场最大右端点和最小左端点即可,因为取这两个端点对应的线段一定是与中间的最大值交集最小的,与两边的交集大小取min就是交集大小了。

对于包含关系,我们发现与最大值交集最小的,一定是包含于他的人中学的最少的人,考虑二维关系不好维护,就将人按照右端点排序,从小到大遍历右端点,在以左端点为下标的树状数组中找比当前左端点大的当中学的最少的人,就是这个最大值对应的答案。

时间复杂度O(nlogn)

解释:代码中对于“比当前左端点大的”这个后缀操作,在离散化左端点时将其反序存储,转化为树状数组的前缀问题。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5,inf = 0x3f3f3f3f;
struct Seg{
	int l,r;
}x[N];
int n,maxl,minr,m,xl[N],cnt = 0,inc[N];
map<int,int> rev;
struct BIT{
	int a[N];
	inline void init()
	{
		for(int i = 1;i <= n;i++) a[i] = inf;
	}
	inline int lowbit(int x)
	{
		return x & (-x);
	}
	inline int query(int x)
	{
		if(!x) return inf;
		return min(query(x - lowbit(x)),a[x]);
	}
	inline void modify(int x,Seg k)
	{
		if(x > cnt) return;
		a[x] = min(a[x],k.r - k.l + 1);
		modify(x + lowbit(x),k);
	}
}p;
inline bool cmp(Seg a,Seg b)
{
	if(a.r ^ b.r) return a.r < b.r;
	else return a.l > b.l;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n>>m;
		maxl = 0,minr = 2e9;cnt = 0;
		rev.clear();
		p.init();
		for(int i = 1;i <= n;i++)
		{
			cin>>x[i].l>>x[i].r;
			xl[i] = x[i].l;
			maxl = max(maxl,x[i].l);
			minr = min(minr,x[i].r);
		}
		sort(xl + 1,xl + n + 1);
		cnt = unique(xl + 1,xl + n + 1) - (xl + 1);
		for(int i = 1;i <= cnt;i++) rev[xl[i]] = cnt + 1 - i;
		sort(x + 1,x + n + 1,cmp);
		for(int i = 1;i <= n;i++)
		{
			inc[i] = p.query(rev[x[i].l]);
			p.modify(rev[x[i].l],x[i]);
		}
		int ans = 0;
		for(int i = 1;i <= n;i++)
		{
			int inter = min(min(max(x[i].r - maxl + 1,0),max(minr - x[i].l + 1,0)),inc[i]);
			ans = max(ans,(x[i].r - x[i].l + 1 - inter) << 1);
		}
		cout<<ans<<endl;
	}
	return 0;
}

E

我们发现如果一个素数是一些数的lcm,当且仅当这个素数单独作为一个元素出现了,所以答案一定不超过第n+1个素数的大小,我们发现第3e5个素数大致在6e6左右。

考虑到一个暴力,对于每个点i开始,暴力向右扫描j,求出[i,j]中元素的lcm并加进桶里,如果lcm大于了第n+1个素数的大小,就停止对j的扫描(因为数更多lcm一定不减)。

这样暴力复杂度是O(n2logn)的,无法通过,我们发现我们只需要检查一个lcm是否出现过,想办法在向右扫的时候跳过lcm相同的一段,这个可以预处理一个lcmst表,然后每次倍增来跳过,理论上比原来还多一个log

按这个写一发,你就会发现你AC了。

因为这样优化后,时间复杂度就有了保证,考虑向右扫时每次lcm的变化都会让lcm的值起码乘上2,所以最多只会变化O(logV)次(V是值域)。单次扫描复杂度就降为了O(lognlogV),总复杂度O(nlognlogV)

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5,M = 6e6 + 5;
typedef long long ll;
ll a[N],st[20][N],n;
int prime[N * 5],tot = 0,lim;
bool vis[M];
inline void init()
{
	for(int i = 2;i <= M - 1;i++)
	{
		if(!vis[i])
			prime[++tot] = i;
		for(int j = 1;j <= tot && 1ll * i * prime[j] <= M - 1;j++)
		{
			vis[i * prime[j]] = 1;
			if(!(i % prime[j])) break;
		}
	}
	memset(vis,0,sizeof(vis));
}
inline int lcm(int x,int y)
{
	if(x == -1 || y == -1) return -1;
	ll p = 1ll * x / __gcd(x,y) * y;
	if(p > lim) return -1;
	return p;
}
inline int fd(int x,int &val)
{
	for(int i = 19;i >= 1;i--)
		if(lcm(val,st[i][x]) == val) x += (1 << i) - 1;
	val = lcm(val,st[0][x + 1]);
	return x + 1;
}
int main()
{
	int T;
	init();
	cin>>T;
	while(T--)
	{
		cin>>n;lim = prime[n + 1];
		for(int i = 1;i <= lim;i++) vis[i] = 0;
		for(int i = 1;i <= n;i++) 
		{
			cin>>a[i];st[0][i] = a[i];
			if(st[0][i] > lim) st[0][i] = -1;
		}
		for(int i = 1;i <= 19;i++)
			for(int j = 1;j <= n;j++)
			{
				if((1 << i) + j - 1 > n) st[i][j] = -1;
				else st[i][j] = lcm(st[i - 1][j],st[i - 1][j + (1 << (i - 1))]);
			}
		for(int i = 1;i <= n;i++)
		{
			int tp = i,now = st[0][i];
			while(tp <= n && now != -1)
			{
				vis[now] = 1;
				tp = fd(tp,now);
			}
		}
		for(int i = 1;i <= lim;i++)
			if(!vis[i]) 
			{
				cout<<i<<endl;
				break;
			}
	}
	return 0;
}

F

咕。

posted @   The_Last_Candy  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示