题解 比赛
真·神仙题
因为我忘了暴力怎么打了直接说正解吧
参考博客
仔细撕烤「第 \(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;
}