CF1835 题解

CF1835 题解

A

考虑到 \(a,b,c > 3\) 的最多只有 \(5\) 个,可以直接循环枚举 \(A\) 。首先将 \(B\) 取到最小值,然后判断 \(C\) 是否达到了最小值,如果没有就将 \(B\)\(C\) 同时补齐。此时特判如果 \(C\) 大于上限(就是 \(\geq 10^{c}\) ),就不对答案贡献。不然在 \(A\) 一定的状态下,可以将 \(B,C\) 同加,作出的贡献就是 \(\min\ \{maxb\ -\ b,maxc\ -\ c\}\) 。如果 \(k\) 不够了就补齐输出即可。

按上述过程模拟,注意细节。

#include<bits/stdc++.h>
using namespace std;
int a,b,c,A,B,C,maxc,maxb,maxa;
long long k;
inline int pow(int x,int tms)
{
	int ret = 1;
	for(int i = 1;i <= tms;i++) ret *= x;
	return ret;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>a>>b>>c>>k;
		maxa = pow(10,a); maxb = pow(10,b); maxc = pow(10,c);
		if(max(a,b) < c - 1){puts("-1");continue;}
		for(A = maxa / 10;A < maxa;A++)
		{
			B = maxb / 10;
			C = A + B;
			if(C < maxc / 10){B += maxc / 10 - C;C = maxc / 10;}
			if(C >= maxc) break;
			if(B >= maxb) continue;
			if(k > min(maxc - C,maxb - B)) k -= min(maxc - C,maxb - B);
			else
			{
				B += k - 1; C += k - 1;
				cout<<A<<" + "<<B<<" = "<<C<<endl;
				k = 0;
				break;
			}
		}
		if(k) puts("-1");
	}
	return 0;
}

B

如果我们确定了最后一个人选的位置 \(x\) ,就可以用一个函数 \(O(\log n)\) 计算出可以中奖的号码对应的区间。方法是二分出左右两边分别到 \(x\) 这一段包含 \(k\) 个点(含自身)的个数。那么对于左边,中奖号码能取的区间就是 \(x\) 和左端点的中点右取整,右边同理。

考虑我们不能枚举每一个 \(x\) 的位置,哪些位置才是有用的。结论是,每个被别人选过的点 \(-2,-1,0,+1,+2\) 。考虑讨论一下两个相邻的别人选的位置,中奖号码是在两个左边,中间,还是两个右边。可以发现不管是哪里靠近两端都是不劣的,\(+2\) 是因为如果中奖号码取到某个特殊的位置,最后一个人取 \(+1\) 时可能和右边的人并列,导致输掉。所以可以 \(+2\) 来规避这个问题。

从左向右询问 \(x\) 可以用双指针完成。时间复杂度 \(O(n\log n)\) 。瓶颈在于排序。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
ll n,m,k,pm = 1,px = 1;
ll a[N];
inline ll calc(ll x)
{
	while(pm <= n && a[pm] < x) ++pm;
	while(px <= n && a[px] <= x) ++px;
	ll l,r;
	l = ((px - k > 0) ? x - (x - a[px - k] + 1) / 2 + 1: 0);
	r = ((pm + k - 1 <= n) ? x + (a[pm + k - 1] - x + 1) / 2 - 1 : m);
	return max(0ll,r - l + 1);
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>m>>k;
	for(int i = 1;i <= n;i++) cin>>a[i];
	a[n + 1] = m + 1;
	sort(a + 1,a + n + 1);
	ll ans = calc(0),mxpos = 0;
	for(ll i = 1,nl,nr;i <= n;i++)
	{
		if(i == 1) nl = max(0ll,a[i] - 2);
		else nl = max(a[i] - 2,a[i - 1] + 3);
		nr = min(m,a[i] + 2);
		for(ll j = nl;j <= nr;j++)
		{
			ll now = calc(j);
			if(now > ans) ans = now,mxpos = j;
		}
	}
	cout<<ans<<" "<<mxpos;
	return 0;
}

C

我们发现数据十分特殊,异或前缀和一下变成是否有 \(4\) 个数 \(a,b,c,d\) ,满足 \(a \ xor \ b = c \ xor\ d\) 。我们将数拆分成二进制前 \(k\) 位和后 \(k\) 位。前 \(k\) 位有至多 \(2^k\) 种数字,我们有 \(2^{k + 1} + 1\) 个前缀(包括 \(0\))。如果我们对于前缀 \(i\) ,找到一个前缀 \(j\) ,两个的前 \(2^k\) 位相等,那么 \([i,j]\) 的异或和就只有后 \(k\) 位。我们先将前面的可能填满,后面还有 \(2^k + 1\) 个前缀,对于后 \(k\) 位就有 \(2^k + 1\) 个取值。因为后 \(k\) 位只有 \(2^k\) 个取值,所以一定能匹配到一个取值,使得后 \(k\) 位异或为 \(0\) ,这样就找到了答案。

所以并不存在 \(-1\) 的情况,异或和 \(hash\) 一下就可以了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 4e5 + 5;
int n,m,jdg = 0;
ll a[N],pre[N],vis[N];
map <ll,pair<int,int> > p;
inline void calc(int l,int r)
{
	if(p.find(a[l - 1] ^ a[r]) == p.end())
	{
		p[a[l - 1] ^ a[r]] = make_pair(l,r);
		return;
	}
	pair <int,int> res = p[a[l - 1] ^ a[r]];
	if(res.second < l) cout<<res.first<<" "<<res.second<<" "<<l<<" "<<r<<endl;
	else if(res.first <= l && l <= res.second) cout<<res.first<<" "<<l - 1<<" "<<res.second + 1<<" "<<r<<endl;
	else if(res.first > l) cout<<l<<" "<<res.first - 1<<" "<<res.second + 1<<" "<<r<<endl;
	jdg = 1;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		p.clear();
		cin>>n; jdg = 0;
		m = (1 << (n + 1));
		for(int i = 1;i <= m;i++) cin>>a[i];
		for(int i = 2;i <= m;i++) a[i] ^= a[i - 1];
		for(int i = 0;i <= m / 2;i++) vis[i] = -1;
		vis[0] = 0;
		for(int i = 1;i <= m;i++)
		{
			if(vis[(a[i] >> n)] != -1) calc(vis[(a[i] >> n)] + 1,i);
			vis[(a[i] >> n)] = i;
			if(jdg) break;
		}
	}
	return 0;
}

D

首先缩点,将原图缩成一些 \(SCC\) 。考虑如果想用每个 \(SCC\) 内部的环绕出一个距离的话,每个环最多绕 \(n\) 次,最多有 \(n\) 个环,环长最多为 \(n\) 。所以当 \(k \geq n^3\) 时,如果有解,一定能绕出来。 \(x,y\) 能互相到达,一定在一个 \(SCC\) 内。

由裴蜀定理可得,一些环长能凑出的长度是 \(gcd\) 的倍数。想要知道每一个环的信息,考虑构造生成树,每条非树边都能贡献一个最小环。由于是在每个 \(SCC\) 内分别 \(dfs\) ,所以没有横叉边,只有返祖边,所以环长等于 \(|dep_u - dep_v - 1|\)

考虑满足条件的 \(x,y\) ,满足 \(dep_u - dep_y \equiv dep_v - dep_i \equiv k\ (\mod d)\)

所以 \(k \equiv 0 \ (\mod d)\) 或者 \(k \equiv \frac d2(\mod d)\) ,第二种情况 \(d\) 为偶数。

开桶分别计算即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
struct Edge{
	int v,next;
}e[N * 2];
typedef long long ll;
ll k;
int d,n,m,head[N],dfn[N],vis[N],cnt = 0,low[N],belong[N],tot = 0,top = 0,s[N],dep[N],pot[N];
vector <int> pts[N];
inline void add(int x,int y)
{
	++tot;
	e[tot].v = y;
	e[tot].next = head[x];
	head[x] = tot;
}
inline void tarjan(int x)
{
	dfn[x] = low[x] = ++tot;
	vis[x] = 1;
	s[++top] = x;
	for(int i = head[x];i;i = e[i].next)
	{
		int to = e[i].v;
		if(!dfn[to])
		{
			tarjan(to);
			low[x] = min(low[x],low[to]);
		}
		else if(vis[to])
			low[x] = min(low[x],dfn[to]);
	}
	if(dfn[x] == low[x])
	{
		++cnt;
		while(s[top + 1] != x)
		{
			belong[s[top]] = cnt;
			pts[cnt].push_back(s[top]);
			vis[s[top]] = 0;
			--top;
		}
	}
}
inline void dfs(int x)
{
	vis[x] = 1;
	for(int i = head[x];i;i = e[i].next)
	{
		int to = e[i].v;
		if(belong[to] != belong[x]) continue;
		if(!vis[to]) 
		{
			dep[to] = dep[x] + 1;
			dfs(to);
		} 
		else
			d = __gcd(d,abs(dep[to] - dep[x] - 1));
	}
}
int main()
{
	cin>>n>>m>>k;
	for(int i = 1,x,y;i <= m;i++)
	{
		cin>>x>>y;
		add(x,y);
	}
	tot = 0;
	for(int i = 1;i <= n;i++) if(!dfn[i]) tarjan(i);
	memset(vis,0,sizeof(vis));
	ll ans = 0;
	for(int i = 1;i <= n;i++)
	{
		if(vis[i]) continue;
		dep[i] = 1;
		d = 0;
		dfs(i);
		if(!d) continue;
		int max_dep = 0;
		for(auto in : pts[belong[i]])
		{
			pot[dep[in]]++;
			max_dep = max(max_dep,dep[in]);
		}
		if(k % d == 0)
		{
			for(int j = 1;j <= max_dep;j++) 
			{
				ans += 1ll * pot[j] * (pot[j] + 1) / 2;
				if(j > d)
				{
					ans += 1ll * pot[j] * pot[j - d];
					pot[j] += pot[j - d];
				}
			}
		}
		else if((k % d) * 2 == d)
		{
			for(int j = d / 2 + 1;j <= max_dep;j++)
			{
				ans += 1ll * pot[j] * pot[j - d / 2];
				pot[j] += pot[j - d];
			}
		}
		fill(pot,pot + max_dep + 1,0);
	}
	cout<<ans;
	return 0;
}

E

学习了洛谷上 Solystic 神犇的做法。

考虑当前策略只和几个信息有关:是否知道 \(backspace\) ,是否知道下一个敲的字母,以及当前敲的是否正确。

  • 如果不知道 \(backspace\)

    • 如果知道下一个敲的字母,直接敲即可。

    • 如果不知道,就随便乱按,按错了就找出 \(backspace\) 然后删掉即可。

  • 如果知道 \(backspace\)

    • 如果知道下一个敲的字母,直接敲。

    • 如果不知道,就随便乱按,按错了就删掉。

我们发现只有遇到新字符是我们才可能乱按。

\(f_{i,j,1/2/3/4}\) 为已经输入了前 \(i\) 个字符,有 \(j\) 个字符知道键位,但是没有输入的期望步数。

1:不知道 \(backspace\) ,当前输入完全正确。

2:不知道 \(backspace\) ,当前输入不正确。

3:知道 \(backspace\) ,不确定是否知道下一个字符。

4:知道 \(backspace\) ,不知道下一个字符。

转移有 \(3 + 4 + 1 + 2 = 10\) 种转移,详见洛谷大佬博客。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5,M = 1005,MOD = 1e9 + 7;
int n,m,a[N],l[M],cnt = 0,vis[M];
typedef long long ll;
ll inv[N],f[M][M][5];
inline void add(ll &x,ll y){x = (x + y) % MOD;}
int main()
{
	cin>>n>>m;
	memset(l,0,sizeof(l));
	for(int i = 1;i <= n;i++) 
	{
		cin>>a[i];
		if(!vis[a[i]]) vis[a[i]] = 1,l[cnt] = i - 1,++cnt;
	}
	l[cnt] = n;
	memset(f,0,sizeof(f));
	inv[1] = 1;
	for(int i = 2;i <= M - 1;i++) inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
	for(int i = cnt - 1;i >= 0;i--)
		for(int j = m - i;j >= 0;j--)
		{
			if(m - i - j > 0)
			{
				add(f[i][j][4],inv[m - i - j] * ((f[i + 1][j][3] + l[i + 1] - l[i]) % MOD) % MOD);
				add(f[i][j][4],(m - i - j - 1) * inv[m - i - j] % MOD * (f[i][j + 1][4] + 2) % MOD);
			}
			
			if(j > 0) add(f[i][j][3],j * inv[m - i] % MOD * ((f[i + 1][j - 1][3] + l[i + 1] - l[i]) % MOD) % MOD);
			add(f[i][j][3],(m - i - j) * inv[m - i] % MOD * f[i][j][4] % MOD);
			
			add(f[i][j][2],(m - i - j) * inv[m - i - j + 1] % MOD * (f[i][j + 1][2] + 2) % MOD);
			if(j > 0) add(f[i][j][2],inv[m - i - j + 1] * (j - 1) % MOD * inv[m - i - 1] % MOD * ((f[i + 1][j - 1][3] + l[i + 1] - l[i]) % MOD) % MOD);
			add(f[i][j][2],inv[m - i - j + 1] * (m - i - j) % MOD * inv[m - i - 1] % MOD * f[i][j][4] % MOD);
			
			if(j == 0)
			{
				add(f[i][0][1],inv[m - i + 1] * ((f[i + 1][0][1] + l[i + 1] - l[i]) % MOD) % MOD);
				add(f[i][0][1],inv[m - i + 1] * (f[i][0][3] + 1 + (i != 0)) % MOD);
				add(f[i][0][1],(m - i - 1) * inv[m - i + 1] % MOD * (f[i][1][2] + 2) % MOD);
			}
		}
	cout<<f[0][0][1];
	return 0;
}

F

咕。

posted @ 2023-09-06 22:05  The_Last_Candy  阅读(8)  评论(0编辑  收藏  举报