CF1863 题解

CF1863 题解

A

条件很简单:如果总共的 '+' 号加上开始上线人数不到 n 人,就不可能。实时记录人数,如果某一时刻大于等于 n 人在线上,就一定是。剩余情况则可能。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,a,q,T;
	cin>>T;
	while(T--)
	{
		cin>>n>>a>>q;
		int flag = (a >= n),sig = a; char c;
		for(int i = 1;i <= q;i++)
		{
			cin>>c;
			if(c == '+') ++sig,++a;
			else --a;
			if(a == n) flag = 1;
		}
		if(flag) puts("YES");
		else if(sig < n) puts("NO");
		else puts("MAYBE");
	}
	return 0;
 } 

B

我们发现,如果 1,2,3,,k 的位置是单增的,一次操作就可以将它们挪到前面,如果 k+1 夹在它们中间,取 k 以外的所有值都无法分开它们,所以答案就是val=2n[posval<posval1]

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n,a[N],p[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n;
		for(int i = 1;i <= n;i++) cin>>p[i],a[p[i]] = i;
		int ans = 0;
		for(int i = 2;i <= n;i++) if(a[i] < a[i - 1]) ++ans;
		cout<<ans<<endl;
	}
	return 0;
}

C

容易发现一开始序列中缺少了一个数,这个数在第二次一定会出现。第一个数被替换后,整个序列的 MEX 就变成了第一个数,于是第二个数又被替换成第一个数...所以相当于在序列末尾增加了一个数,每操作一次相当于将起点向左移了一个,最后从起点开始向右移 k 次即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n,k,a[N],vis[N];
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n>>k;
		for(int i = 1;i <= n;i++) cin>>a[i];
		fill(vis + 0,vis + n + 1,0);
		for(int i = 1;i <= n;i++) vis[a[i]] = 1;
		for(int i = 0,orn = n;i <= orn;i++) if(!vis[i]) a[++n] = i;
		k %= n;
		if(k == 0) for(int i = 1;i <= n - 1;i++) cout<<a[i]<<" ";
		else 
			for(int i = n - k + 1,c = 1;c <= n - 1;i++,c++)
			{
				if(i == n + 1) i = 1;
				cout<<a[i]<<" ";
			}
		cout<<endl; 
	}
	return 0;
}

D

第一眼看出各种奇怪思路,然后仔细发现一个性质:每一行和每一列要填多少白格是定的,如果每行/列要填的格子是奇数,就无解。对于一行,横着的骨牌一定会一黑一白,所以其实不影响,所以只有竖着的骨牌影响。

推到这里我们发现此题行列不制约,所以对行和列单独做,贪心染白色即可,如果染不下去就无解。

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5,M = 505;
char s[M][M],cor[M][M];
vector <int> c[M],d[M];
int n,m,cnt = 0,numc[M],numd[M];
inline bool solverow()
{
	for(int i = 1;i <= n;i++)
	{
		if(numc[i] & 1) return false;
		numc[i] /= 2; 
	}
	for(int i = 1;i <= n - 1;i++)
	{
		for(vector <int> :: iterator it = c[i].begin();it != c[i].end();it++)
		{
			int now = (*it);
			if(!numc[i])
			{
				if(!numc[i + 1]) return false;
				else numc[i + 1]--,cor[i][now] = 'B',cor[i + 1][now] = 'W';
			}
			else
			{
				cor[i][now] = 'W'; cor[i + 1][now] = 'B';
				numc[i]--;
			}
		}
	}
	return true;
}
inline bool solvecolumn()
{
	for(int i = 1;i <= m;i++)
	{
		if(numd[i] & 1) return false;
		numd[i] /= 2; 
	}
	for(int i = 1;i <= m - 1;i++)
	{
		for(vector <int> :: iterator it = d[i].begin();it != d[i].end();it++)
		{
			int now = (*it);
			if(!numd[i])
			{
				if(!numd[i + 1]) return false;
				else numd[i + 1]--,cor[now][i] = 'B',cor[now][i + 1] = 'W';
			}
			else
			{
				cor[now][i] = 'W'; cor[now][i + 1] = 'B';
				numd[i]--;
			}
		}
	}
	return true;
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n>>m;
		for(int i = 1;i <= n;i++) numc[i] = 0,c[i].clear();
		for(int i = 1;i <= m;i++) numd[i] = 0,d[i].clear();
		for(int i = 1;i <= n;i++)
			for(int j = 1;j <= m;j++)
			{
				cin>>s[i][j];
				if(s[i][j] == 'U') c[i].push_back(j),++numc[i],++numc[i + 1];
				if(s[i][j] == 'L') d[j].push_back(i),++numd[j],++numd[j + 1];
			}
		if(!solverow()) {puts("-1"); continue;}
		if(!solvecolumn()) {puts("-1"); continue;}
		for(int i = 1;i <= n;i++,cout<<endl)
			for(int j = 1;j <= m;j++)
			{
				if(s[i][j] == '.') cor[i][j] = '.';
				cout<<cor[i][j]; 
			}
	}
	return 0;
}

E

按照拓扑序推一遍,得到一个答案,我们发现我们可以将开始的几个推到第二天去,虽然结束时间可能增多,但是开始时间变晚,可能答案更优。我们又发现这个最多只会推一天,不然没意思。

由于开始的几个(入度为 0 的几个)最多只向后推一天,所以每一个事件都只可能向后延一天,将开始点按照 hi 排序,所以每次更改后搜索,记录这个事件是否向后推过,如果推过或者本来就不用推,就停止搜索。这样复杂度是 Θ(n) 的(不算对开始点的排序)。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
vector <int> G[N];
int k,m,deg[N],a[N],n,cnt = 0,line[N],vis[N];
typedef long long ll;
ll t[N],ans = 0,maxn = 0;
inline bool cmp(int x,int y){return a[x] < a[y];}
inline void dfs(int x,int last)
{
	maxn = max(maxn,t[x]);
	for(auto to : G[x])
	{
		if(vis[to]) continue;
		if(t[to] < t[x]) {t[to] += k; vis[to] = 1; dfs(to,x);}
	}
}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n>>m>>k;
		cnt = 0;
		for(int i = 1;i <= n;i++) cin>>a[i],G[i].clear(),deg[i] = 0;
		fill(t + 1,t + n + 1,0);
		fill(vis + 1,vis + n + 1,0);
		for(int i = 1,x,y;i <= m;i++)
		{
			cin>>x>>y;
			G[x].push_back(y); ++deg[y];
		}
		queue <int> q;
		for(int i = 1;i <= n;i++) if(!deg[i]) line[++cnt] = i,q.push(i),t[i] = a[i];
		sort(line + 1,line + cnt + 1,cmp);
		while(!q.empty())
		{
			int x = q.front();
			q.pop();
			for(auto to : G[x])
			{
				if(a[to] >= a[x]) t[to] = max(t[to],t[x] + a[to] - a[x]);
				else t[to] = max(t[to],t[x] + a[to] + k - a[x]);
				--deg[to];
				if(!deg[to]) q.push(to); 
			}
		}
		maxn = 0;
		for(int i = 1;i <= n;i++) maxn = max(maxn,t[i]);
		ans = maxn - t[line[1]];
		for(int i = 1;i <= cnt - 1;i++) 
		{
			t[line[i]] += k;
			dfs(line[i],0);
			ans = min(ans,maxn - t[line[i + 1]]);
		}	
		cout<<ans<<endl;
	}
	return 0;
}

F

考虑 Θ(n3) dp ,设 fl,r 表示 [l,r] 能否被表示出来,每次枚举断点 k ,更新 fl,k,fk+1,r

再考虑如果 fl,r 能在 k 处分开,有什么性质: 假设第 p 位是 suml,rhighbit ,那么 p 一定是 fl,kfk+1,r 第一个不一样的地方,此时谁取 1 谁就更大。所以我们记 Li 为左端点在 i 的区间,并且能被表示出的区间中 highbit 的集合,Ri 为右端点,同理。按照长度从大到小排序,所以当前区间转移时,可以用的区间一定包含当前区间,所以 sumi,j&Li>0 或者 sumi,j&Ri>0[i,j] 可以转移到的充要条件,按此转移即可。

注意 sumi,j=highbit=0 时也可以转移,所以记录一个 “广义零值” 来区分“没有转移” 和 “转移为零“。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
typedef long long ll;
const ll ZERO = 1ll << 61;
ll a[N],sum[N],L[N],R[N];
int n,ans[N];
inline ll highbit(ll x) {return (x == ZERO) ? ZERO : (1ll << (63 - __builtin_clzll(x)));} 
inline ll getsum(int l,int r) {return (sum[r] ^ sum[l - 1]) ? (sum[r] ^ sum[l - 1]) : ZERO;}
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>n;
		for(int i = 1;i <= n;i++) cin>>a[i];
		sum[0] = 0;
		for(int i = 1;i <= n;i++) sum[i] = sum[i - 1] ^ a[i];
		fill(L + 1,L + n + 1,0); fill(R + 1,R + n + 1,0);
		L[1] = highbit(getsum(1,n)); R[n] = highbit(getsum(1,n)); ans[1] = 1;
		for(int len = n - 1;len >= 1;len--)
		{
			for(int i = 1;i + len - 1 <= n;i++)
			{
				int j = i + len - 1;
				ll jdg = ((getsum(i,j) | ZERO) & (L[i] | R[j]));
				if(jdg) L[i] |= highbit(getsum(i,j)),R[j] |= highbit(getsum(i,j));
				if(len == 1) ans[i] = (jdg > 0);
			}
		}
		for(int i = 1;i <= n;i++) cout<<ans[i];
		cout<<endl;
	}
	return 0;
 } 

G

咕。

H

考虑 dp 方程: fx=2max{flc,frc}[dplcdprc] 。因为每次落下来的饼干是均匀分给左右的,所以次数是两倍的两边最大值,如果不一样则先给大的那一个,最后就可以少走一次。

dx=dpx1 ,则 dx=2max{dlc,drc}+[dlc==drc] ,记 depx 为深度, Dx=dx×2depx 。得到 Dx=max{Dlc,Drc}+2depx[Dlc==Drc]

最后答案就是 1+maxDx ,但是不可能用一个数存下 D ,考虑 D 一路向上,每次最多附有一个 1 ,最多有 logai+logn1 ,因为单个点可以有 logai1 ,每次合并增加 1 需要两个相同的值。

用一个集合存储 D ,存其中二进制为 1 的个数,我们发现 1 是一个个向上推的,所以 uvD 相同,则会在 lca 处贡献一个 D+2deplca 。一开始将叶子初始化为 (av1)×2depv 。每加入一个点往上更新 lca 即可,由于每次增加一个 1 ,所以不会超过 log 次。,每次修改的时候,删除原数的贡献,再增加新数的贡献即可,复杂度 Θ(nlog2n)

#include<bits/stdc++.h>
#define int long long
using namespace std;
#define ll long long
#define vec vector<int>
const int N = 2e5 + 5,MOD = 998244353,inf = 1e18;
vec G[N];
int cnt = 0,h[N],rev[N],dep[N],n,q,dfn[N],fa[22][N],lg[N];
ll pw[N];
set <pair<vec,int> > s;
inline int maxdep(int x,int y) {return dep[x] > dep[y] ? x : y;}
inline int mindep(int x,int y) {return dep[x] < dep[y] ? x : y;}
inline void dfs(int x,int last)
{
	dfn[x] = ++cnt;
	rev[cnt] = x;
	fa[0][dfn[x]] = last;
	dep[x] = dep[last] + 1;
	for(auto to : G[x])
		dfs(to,x);
}
inline int lca(int x,int y)
{
	if(x == y) return x;
	x = dfn[x]; y = dfn[y];
	if(x > y) swap(x,y);
	int nlg = lg[y - x + 1];
	return mindep(fa[nlg][x],fa[nlg][y - pw[nlg] + 1]);
} 
inline int nxt(auto it)
{
	auto id = next(it),ip = prev(it);
	int ret = 0; 
	if((*id).first == (*it).first) ret = maxdep(ret,lca((*it).second,(*id).second));
	if((*ip).first == (*it).first) ret = maxdep(ret,lca((*it).second,(*ip).second));
	return ret;
}
inline void ist(int x)
{
	if(!h[x]) return;
	vec now;
	for(int i = 30;i >= 0;i--) if(((h[x] - 1) >> i) & 1) now.push_back(dep[x] + i);
	auto it = s.insert(make_pair(now,x)).first;
	while(it != s.end())
	{
		int nc = nxt(it);
		if(!nc) break;
		now.push_back(dep[nc]);
		it = s.insert(make_pair(now,nc)).first;
	}
}
inline void del(int x)
{
	if(!h[x]) return;
	vec now;
	for(int i = 30;i >= 0;i--) if(((h[x] - 1) >> i) & 1) now.push_back(dep[x] + i);
	auto it = s.find(make_pair(now,x));
	while(it != s.end())
	{
		int q = nxt(it);
		s.erase(it);
		if(!q) break;
		now.push_back(dep[q]);
		it = s.find(make_pair(now,q));
	}
}
inline ll Query()
{
	if(s.size() == 2) return 0;
	ll res = 1;
	auto it = s.end(); it--; it--;
	for(auto in : (*it).first) res += pw[in],res %= MOD;
	return res;
}
signed main()
{
	cin>>n;
	s.insert(make_pair(vec{inf},0)); s.insert(make_pair(vec{-1},0));
	pw[0] = 1;
	for(int i = 1;i <= N - 1;i++) pw[i] = pw[i - 1] * 2 % MOD;
	lg[0] = -1;
	for(int i = 1;i <= N - 1;i++) lg[i] = lg[i >> 1] + 1;
	for(int i = 2,x;i <= n;i++) cin>>x,G[x].push_back(i);
	for(int i = 1;i <= n;i++) cin>>h[i];
	dep[0] = -1; dep[n + 1] = inf;
	dfs(1,0);
	for(int i = 1;i <= 20;i++)
		for(int j = 1;j + pw[i] - 1 <= n;j++)
			fa[i][j] = mindep(fa[i - 1][j],fa[i - 1][j + pw[i - 1]]);
	for(int i = 1;i <= n;i++) ist(i);
	cout<<Query()<<endl;
	cin>>q;
	for(int i = 1,x,v;i <= q;i++)
	{
		cin>>x>>v;
		del(x);
		h[x] = v;
		ist(x);
		cout<<Query()<<endl;
	}
	return 0;
}

I

咕。

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