CSP-S 2024 题解
CSP-S 2024 题解
决斗
发现一张卡只有和小的配对才能有贡献,所以sort一遍维护一下比当前小,没被击败的卡的数量就好了
其实可以证明答案就是 \(n-众数个数\) ,大概就是贪心的选择一条升序的序列,之后它们有 \(len-1\) 的贡献(即链头没有贡献),发现一共正好有众数个数条链
超速检测
挂得最狠的一集
将所有超速区间求出来,二分求能被查到的装置区间就好了
中间需要注意想办法避免一下精度误差或者设一下 \(eps\) (再也不用 long double 不设 \(eps\) 了)
染色
状态设计典中典,直接设 \(f_{i,j}\) 为到第 \(i\) 个位置,上一个和 \(i-1\) 颜色不相同的位置的数是 \(j\) 的最大权值
方程显然,就是 \(f_{i,a_{i-1}}=\max(f_{i-1,a_i}+a_i,f_{i,j})\),\(f_{i,j}=\max([a_i=a_{i-1}]a_i+f_{i-1,j},f_{i,j})\)
发现滚掉一维之后相当于全局加,全局查最大值,单点取 \(\max\),全局 \(\max\) 是单调的,这个玩意打个标记随便维护,当然也可以写线段树
直接打标记是 \(O(Tn)\) 的,离散化之后线段树是 \(O(Tn \log n)\) 的,直接动态开点是 \(O(Tn \log V)\) 的
擂台游戏
事实上 \(T n\log n\) 能拿 \(92\),赛时谁一分没有我不说
考虑一下性质,发现可以建出来一个类似线段树的胜负树,这个比较显然
对于 \(O(T nm\log n)\),可以每次遇到询问暴力修改线段树,简单来说就是先将所有自由位置设成 \(0\),之后依次想让谁赢就把他设成 \(inf\),直接暴力合并即可
接下来直接考虑拆贡献,发现一个人能赢的充要条件是他能赢到根路上所有自己是擂主的比赛,并且所有自己不是擂主的比赛中,对方子树内有方案使得对方在这一场输
首先前面很好判定,从底到根暴力跳即可,和条件二分开做就好了,关键是后面那一坨的贡献
考虑没有自由位置的怎么做 暴力模拟一遍 其实就是发现一个节点作擂主输的位置是它的关键点,一个节点到关键点一定满足条件一,也就是说除非这个节点到关键点的路径上不满足条件二,它都能对关键点有一个 "可能输的方案",而如果一个节点没有 "可能输的方案",则它的兄弟不满足条件二,而如果这个节点关键点到根不满足条件二,则关键点和这个节点本身都没有贡献
所以依旧是对每一层的点递归做,对于 不满足条件二 的点直接删除它的贡献即可
考虑一下自由位置,其实就是每个祖先擂主节点都是其关键点,所以对每个节点维护其子树内还有没有满足条件二的自由节点即可
由于每个位置只会删除一次,我们这就获得了 \(O(T n\log n)\) 做法,复杂度瓶颈在于对每个节点寻找关键点
所以直接dfs预处理出每个点的关键点即可做到 \(O(Tn)\)
贴个代码
#include<bits/stdc++.h>
using namespace std;
using llt=long long;
const llt N=200100;
llt n,m,k,T,a[N],b[N],c[N],X[N],P[N],node[N<<2],h[N<<2],Leaf,ans,output,save[N],B,us[N<<2],Get[N<<2];
bool Host[N<<2],tag[N<<2],is[N<<2],vis[N<<2];char C[32][N<<2];vector<llt> vec[N];
void build(llt now)
{
h[now]=1;is[now]=1,vis[now]=node[now]=tag[now]=0;
if(now<=Leaf)
{
build(now<<1),build(now<<1|1);
h[now]=h[now<<1]+1;
}
if(now&1) Host[now]=(C[h[now]][(now-(1<<k-h[now]+1))>>1]=='1');
else Host[now]=(C[h[now]][(now-(1<<k-h[now]+1))>>1]=='0');
}
void dfs(llt now)
{
if(Host[now]) us[h[now]]=now;
else us[h[now]]=us[h[now]+1];
if(now>Leaf) {Get[now]=us[min(b[now-Leaf]+1,N+1)];return;}
dfs(now<<1);dfs(now<<1|1);
}
void Delete(llt now,llt id)
{
if(tag[now]) return;
tag[now]=1;is[now]=0;
if(now>Leaf)
{
llt u=Get[now];
if((now-Leaf<=1<<B)&&!vis[now]) ans-=now-Leaf;
if(now-Leaf<=id) node[u]--;
}
if(now<=Leaf) Delete(now<<1,id),Delete(now<<1|1,id);
}
void update(llt now,llt id)
{
if(is[now]==0||!now) return;
is[now]=is[now<<1]|is[now<<1|1];
if(!is[now]&&!node[now]&&Host[now]&&!tag[now]) Delete(now^1,id);
if(!is[now]) update(now>>1,id);
}
int main()
{
freopen("arena.in","r",stdin);
freopen("arena.out","w",stdout);
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=m;i++) scanf("%lld",&c[i]);
for(k=0;(1<<k)<n;k++);Leaf=(1<<k)-1;
for(int i=1;i<=k;i++)
for(int j=0;j<1<<k-i;j++)
scanf(" %c",&C[i][j]);
scanf("%lld",&T);h[0]=10010;
while(T--)
{
build(1);
for(int i=0;i<4;i++) scanf("%lld",&X[i]);
for(int i=1;i<=n;i++) b[i]=a[i]^X[i%4];B=0;ans=1;
dfs(1);
for(int i=1;i<=n;i++)
{
if((1<<B)<i)
{
B++;
for(int j=i;j<=(1<<B);j++)
ans+=j*(!tag[Leaf+j]);
for(auto v:vec[B]) if(!tag[v]&&!vis[v]) ans-=v-Leaf,vis[v]=tag[v]=1;
}
if(!tag[Leaf+i])
{
llt u=Get[Leaf+i];
vec[h[u]].push_back(Leaf+i),node[u]++;
update(Leaf+i,i);if(h[u]<=B) ans-=i,vis[Leaf+i]=1;
}
save[i]=ans;
}
for(int i=1;i<=k;i++) vector<llt>().swap(vec[i]);vector<llt>().swap(vec[h[0]]);
output=0;
for(int i=1;i<=m;i++) output^=(i*save[c[i]]);
printf("%lld\n",output);
}
return 0;
}