题解 比赛

传送门

真·神仙题

因为我忘了暴力怎么打了直接说正解吧
参考博客

仔细撕烤「第 \(i\) 位选手会发动技能,当且仅当他发动了技能后会获胜,不发动时不会获胜」这句话
又注意到选手按编号从小到大依次发动技能
那么对于一个人 \(k\),若 \(x=k-a_k\pmod n\)
因为在前面发动技能的人编号 \(<k\),且每个人只会改成自己的编号
所以只在 \(x<k\) 时第 \(k\) 个人可能发动技能
那么在这种情况下我们从 \(k-a_k\)\(k\) 连一条边
在最后得到的 0 所在的树上做一个 DP
\(f_i\) 为 0 时 \(i\) 必胜,为 1 时 \(i\) 必败
则有

\[f_u=1-\prod\limits_{v\in son_u}f_v \]

于是最后 0 必败则答案为 \(\min\{v\mid v\in son_0,f_v=1\}\)
否则答案为 0

现在考虑加上修改操作
发现就相当于加上了 link-cut
考虑 LCT 维护 DDP
并不知道如何推出转移矩阵(
所以有一个类似一次函数的写法
维护 \(lit_i\)\(i\) 的虚儿子中 f 值为 0 的个数
\(x\)\(i\) 重儿子的 DP 值,令 \(f_i=kx+b\)
\(lit_i>0\) 时,\(f_i=0*x+1=1\)
\(lit_i=0\) 时,\(f_i=-x+1\)
于是就可以转移了
然后考虑怎么在 LCT 上维护这个东西
首先可以维护一个 set 记录 0 的所有 DP 值为 0 的儿子
为了维护这个东西,每次 link 和 cut 就都要考虑一下对辅助树根的影响
这个可以用 access 维护

  • set 的 insert 和 erase 如果传入值的话是可以传入重复/不存在的值的

细节还是较多的,尤其注意一个点的 DP 值并不是定值,而是与这个点的相对位置相关
复杂度 \(O(n\log n)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 300010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
int a[N];

// namespace table{
// 	int f[1010][1010];
// 	void solve() {
// 		for (int i=0; i<n; ++i) f[n][i]=i;
// 		for (int i=n-1; ~i; --i)
// 			for (int j=0; j<n; ++j)
// 				if (f[i+1][(j+a[i])%n]==i) f[i][j]=i;
// 				else f[i][j]=f[i+1][j];
// 		cout<<"f: "<<endl;
// 		for (int i=0; i<n; ++i) {for (int j=0; j<n; ++j) cout<<f[i][j]<<' '; cout<<endl;}
// 	}
// }

namespace force{
	int pos[N], sta[N], top;
	set<int> s[N];
	int recalc() {
		for (int i=0; i<n; ++i) s[i].clear();
		for (int i=0; i<n; ++i) s[i].insert(i), pos[i]=i;
		for (int i=n-1; ~i; --i) {
			top=0;
			for (auto it:s[i]) {
				int t=((it-a[i])%n+n)%n;
				if (pos[t]!=i) {
					s[pos[t]].erase(t);
					sta[++top]=t;
				}
			}
			for (int j=1; j<=top; ++j) pos[sta[j]]=i, s[i].insert(sta[j]);
		}
		return pos[0];
	}
	void solve() {
		printf("%d\n", recalc());
		for (int i=1,x,y; i<=m; ++i) {
			x=read(); y=read();
			a[x]=y;
			printf("%d\n", recalc());
		}
	}
}

namespace task1{
	set<int> s[N];
	int head[5010], ans[5010], f[5010], ecnt;
	struct edge{int to, next;}e[N];
	inline void add(int s, int t) {e[++ecnt]={t, head[s]}; head[s]=ecnt;}
	void dfs(int u) {
		ll prod=1;
		s[u].clear();
		for (int i=head[u],v; ~i; i=e[i].next) {
			v = e[i].to;
			dfs(v);
			prod*=f[v];
			if (!f[v]) s[u].insert(v);
		}
		f[u]=1-prod;
	}
	void rebuild() {
		memset(head, -1, sizeof(head)); ecnt=0;
		memset(ans, 127, sizeof(ans));
		for (int i=0; i<n; ++i) {
			int t=(i-a[i]+n)%n;
			if (t<i) add(t, i);
		}
		dfs(0);
		if (!f[0]) puts("0");
		else printf("%d\n", *s[0].begin());
	}
	void solve() {
		rebuild();
		for (int i=1,x,y; i<=m; ++i) {
			x=read(); y=read();
			a[x]=y;
			rebuild();
		}
	}
}

namespace task{
	set<int> ans;
	int fa[N], son[N][2], lit[N];
	struct line{
		int k, b;
		line(int x=0, int y=0){k=x; b=y;}
		inline int qval(int x) {return k*x+b;}
		inline line operator * (line t) {return line(k*t.k, k*t.b+b);}
	}dp[N];
	#define fa(a) fa[a]
	#define dp(a) dp[a]
	#define son(a, b) son[a][b]
	#define loc(a) (son(fa(a), 1)==a)
	#define isrot(a) (son(fa(a), 0)!=a&&son(fa(a), 1)!=a)
	inline void pushup(int a) {
		dp[a]=line(lit[a]?0:-1, 1);
		if (son(a, 0)) dp[a]=dp[son(a, 0)]*dp[a];
		if (son(a, 1)) dp[a]=dp[a]*dp[son(a, 1)];
	}
	void ror(int x) {
		int y=fa(x), z=fa(y), k=loc(x);
		if (!isrot(y)) son(z, loc(y))=x; fa(x)=z;
		son(y, k)=son(x, k^1); fa(son(x, k^1))=y;
		son(x, k^1)=y; fa(y)=x;
		pushup(y); pushup(x);
	}
	void splay(int x) {
		for (int f; f=fa(x),!isrot(x); ror(x))
			if (!isrot(f)) loc(x)^loc(f)?ror(x):ror(f);
	}
	int findrt(int x) {
		while (son(x, 0)) x=son(x, 0);
		splay(x);
		return x;
	}
	void access(int x) {
		int lst;
		for (lst=0; x; lst=x,x=fa(x)) {
			splay(x);
			if (son(x, 1)&&!dp[son(x, 1)].qval(1)) ++lit[x];
			if (lst&&!dp[lst].qval(1)) --lit[x];
			son(x, 1)=lst; pushup(x);
		}
		if (findrt(lst)==1 && son(1, 1)) {
			x=findrt(son(1, 1)); splay(1);
			// cout<<"x: "<<x<<endl;
			if (!dp[x].qval(1)) ans.insert(x);
			else ans.erase(x);
		}
	}
	void link(int x, int y) {
		// cout<<"link: "<<x<<' '<<y<<endl;
		splay(x); assert(!son(x, 0)); access(y);
		// cout<<"dp: "<<x<<' '<<dp[x].k<<' '<<dp[x].b<<endl;
		if (!dp[x].qval(1)) ++lit[y];
		fa(x)=y; pushup(y); access(x);
	}
	void cut(int x, int y) {
		access(x); splay(y);
		if (y==1&&!dp[x].qval(1)) ans.erase(x);
		fa(x)=son(y, 1)=0;
		pushup(y); access(y);
	}
	void solve() {
		for (int i=1; i<=n; ++i) pushup(i);
		for (int i=0; i<n; ++i) {
			int t=(i-a[i]+n)%n;
			if (t<i) link(i+1, t+1);
		}
		// link(2, 1); link(3, 1); link(4, 2);
		// cout<<"i  : "; for (int i=1; i<=n; ++i) cout<<setw(2)<<i<<' '; cout<<endl;
		// cout<<"fa : "; for (int i=1; i<=n; ++i) cout<<setw(2)<<fa[i]<<' '; cout<<endl;
		// cout<<"ls : "; for (int i=1; i<=n; ++i) cout<<setw(2)<<son(i, 0)<<' '; cout<<endl;
		// cout<<"rs : "; for (int i=1; i<=n; ++i) cout<<setw(2)<<son(i, 1)<<' '; cout<<endl;
		// cout<<"lit: "; for (int i=1; i<=n; ++i) cout<<setw(2)<<lit[i]<<' '; cout<<endl;
		// cout<<"ans: "; for (auto it:ans) cout<<it<<' '; cout<<endl;
		splay(1); printf("%d\n", dp[1].qval(1)?*ans.begin()-1:0);
		for (int i=1,x,y,t; i<=m; ++i) {
			// cout<<"i: "<<i<<endl;
			x=read(); y=read();
			t=(x-a[x]+n)%n;
			if (t<x) cut(x+1, t+1);
			a[x]=y;
			t=(x-a[x]+n)%n;
			if (t<x) link(x+1, t+1);
			splay(1); printf("%d\n", dp[1].qval(1)?*ans.begin()-1:0);
			// cout<<"i  : "; for (int i=1; i<=n; ++i) cout<<setw(2)<<i<<' '; cout<<endl;
			// cout<<"fa : "; for (int i=1; i<=n; ++i) cout<<setw(2)<<fa[i]<<' '; cout<<endl;
			// cout<<"ls : "; for (int i=1; i<=n; ++i) cout<<setw(2)<<son(i, 0)<<' '; cout<<endl;
			// cout<<"rs : "; for (int i=1; i<=n; ++i) cout<<setw(2)<<son(i, 1)<<' '; cout<<endl;
			// cout<<"lit: "; for (int i=1; i<=n; ++i) cout<<setw(2)<<lit[i]<<' '; cout<<endl;
			// cout<<"ans: "; for (auto it:ans) cout<<it<<' '; cout<<endl;
		}
	}
}

signed main()
{
	freopen("match.in", "r", stdin);
	freopen("match.out", "w", stdout);

	n=read(); m=read();
	for (int i=0; i<n; ++i) a[i]=read();
	// force::solve();
	task::solve();
	
	return 0;
}
posted @ 2022-03-04 21:01  Administrator-09  阅读(1)  评论(0编辑  收藏  举报