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,\dots,k\) 的位置是单增的,一次操作就可以将它们挪到前面,如果 \(k + 1\) 夹在它们中间,取 \(k\) 以外的所有值都无法分开它们,所以答案就是\(\sum_{val = 2}^n [pos_{val} < pos_{val - 1}]\)

#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\) 的几个)最多只向后推一天,所以每一个事件都只可能向后延一天,将开始点按照 \(h_i\) 排序,所以每次更改后搜索,记录这个事件是否向后推过,如果推过或者本来就不用推,就停止搜索。这样复杂度是 \(\Theta(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

考虑 \(\Theta(n^3)\ dp\) ,设 \(f_{l,r}\) 表示 \([l,r]\) 能否被表示出来,每次枚举断点 \(k\) ,更新 \(f_{l,k},f_{k + 1,r}\)

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

注意 \(sum_{i,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\) 方程: \(f_x = 2\max\{f_{lc},f_{rc}\} - [dp_{lc} \neq dp_{rc}]\) 。因为每次落下来的饼干是均匀分给左右的,所以次数是两倍的两边最大值,如果不一样则先给大的那一个,最后就可以少走一次。

\(d_x = dp_x - 1\) ,则 \(d_x = 2\max\{d_{lc},d_{rc}\} + [d_{lc} == d_{rc}]\) ,记 \(dep_x\) 为深度, \(D_x = d_x\times 2^{dep_x}\) 。得到 \(D_x = \max\{D_{lc},D_{rc}\} + 2^{dep_x}[D_{lc} == D_{rc}]\)

最后答案就是 \(1 + \max{D_x}\) ,但是不可能用一个数存下 \(D\) ,考虑 \(D\) 一路向上,每次最多附有一个 \(1\) ,最多有 \(\log a_i + \log n\)\(1\) ,因为单个点可以有 \(\log a_i\)\(1\) ,每次合并增加 \(1\) 需要两个相同的值。

用一个集合存储 \(D\) ,存其中二进制为 \(1\) 的个数,我们发现 \(1\) 是一个个向上推的,所以 \(u\)\(v\)\(D\) 相同,则会在 \(lca\) 处贡献一个 \(D + 2^{dep_{lca}}\) 。一开始将叶子初始化为 \((a_v - 1) \times 2^{dep_v}\) 。每加入一个点往上更新 \(lca\) 即可,由于每次增加一个 \(1\) ,所以不会超过 \(\log\) 次。,每次修改的时候,删除原数的贡献,再增加新数的贡献即可,复杂度 \(\Theta(n\log^2 n)\)

#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 @ 2023-09-25 21:28  The_Last_Candy  阅读(16)  评论(0编辑  收藏  举报