题解:P17725 [SCP-S 2026] 大雨 / rain
题目链接。
考虑
否则
段内,考虑任意的
记这样求得的答案为
更进一步,我们给前
若
所以我们只考虑
考虑如何从
钦定
对于被钦定的部分的合并可以用位运算。所以
看眼题解,发现怎么直接在 trie 树上打标记就做完了。
还是从查询出发。如果总节点数足够少,查询时,如果我们走到一棵子树里,发现这棵子树里居然没有合法点,那就可以直接把这整棵子树删除了。均摊就是对的。
修改时,我们有一些子树是满的,我们把满的子树缩成一个点,这个点下方只考虑满的情况即可。
对于一个满点,记录其左端点
所以其实根本不需要打标记,其实就是维护虚树。。。
现在我们要知道
:::info[代码]
#include <bits/stdc++.h>
using namespace std;
namespace tymxz{
const int N=1e6+7,LG=30;
int n;
int a[N],vs[N],L[N];
int v[30][2],cnt;
void work(int p,int x){
int j=vs[p];
if(j==-1) return;
int o=(a[p]>>j)&1;
if(v[j][0]&&v[j][1]) cnt--;
v[j][o]+=x;
if(v[j][0]&&v[j][1]) cnt++;
}
int dp[N],g[N];
int trie[N*LG][2],tot;
int c[N*LG];
void chmax(int &x,int y){
if(x<y) x=y;
}
void init(){
tot=1;
c[tot]=0;
}
void ins(int &r,int a,int b,int id){//a<=b^x
if(r==0) r=tot=1,trie[tot][0]=trie[tot][1]=c[tot]=0;
int p=1;
for(int i=LG-1;i>=0;i--){
int a1=(a>>i)&1,b1=(b>>i)&1;
if(a1==0){
if(!trie[p][b1^1]) trie[p][b1^1]=++tot,trie[tot][0]=trie[tot][1]=c[tot]=0;
chmax(c[trie[p][b1^1]],id+1);
}
if(!trie[p][a1^b1]) trie[p][a1^b1]=++tot,trie[tot][0]=trie[tot][1]=c[tot]=0;
p=trie[p][a1^b1];
}
chmax(c[p],id+1);
}
const int inf=(1<<30)+1;
int dl[N][2],dr[N][2],nw,X,ans;
int solve(int u,int v,int x,int y){
if(dp[u]+1!=dp[v]) return inf;
x|=dl[u][0]|dr[v][0];
y|=dl[u][1]|dr[v][1];
if(x&y) return inf;
return (x^X)&(x|y);
}
void qry(int &p,int dep,int x,int y){
if(p==0) return;
if(c[p]>0){
int j=c[p]-1;
int w=solve(j,nw,x,y);
if(w<inf) ans=min(ans,w);
else c[p]=0;
}
if(dep==-1){
if(c[p]==0) p=0;
return;
}
int i=(X>>dep)&1;
qry(trie[p][i],dep-1,x|((i!=0)<<dep),y|((i!=1)<<dep));
if(trie[p][i]==0){
i^=1;
qry(trie[p][i],dep-1,x|((i!=0)<<dep),y|((i!=1)<<dep));
}
if(trie[p][0]==0&&trie[p][1]==0&&c[p]==0) p=0;
return;
}
inline int turn(int p,int o){
p++;
if(vs[p]==-1) return 0;
if(o!=((a[p]>>vs[p])&1)) return 0;
return (1<<vs[p]);
}
bool chk(){
tot=0;
int rt=0;
for(int l=0;l<=n;){
int r=l;
while(r<=n&&dp[r]==dp[l]) r++;
for(int i=l;i<r;i++){
if(i>0){
nw=i,ans=inf,X=a[i];
qry(rt,LG-1,0,0);
g[i]=ans;
}
}
rt=0;
for(int i=l;i<r;i++){
if(i<n){
if(g[i]<inf) ins(rt,g[i],a[i+1],i);
}
}
l=r;
}
for(int i=n-1;i>=0;i--){
if(dp[i]<dp[n]&&g[i]<=a[i+1]) return 1;
if(a[i]>a[i+1]) return 0;
}
return 0;
}
void subt(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cnt=0;
for(int i=0;i<LG;i++) v[i][0]=v[i][1]=0;
for(int i=2;i<=n;i++){
if(a[i]!=a[i-1]) vs[i]=__lg(a[i]^a[i-1]);
else vs[i]=-1;
}
int l=1;
for(int i=1;i<=n;i++){
if(i>1) work(i,1);
while(l<i&&cnt>0) work(l+1,-1),l++;
dp[i]=dp[l-1]+1;
L[i]=l;
}
for(int i=0;i<=n;i++) for(int o=0;o<2;o++){
if(i>1&&dp[i]==dp[i-1]) dr[i][o]=dr[i-1][o]|turn(i-1,o);
else dr[i][o]=0;
}
for(int i=n;i>=0;i--) for(int o=0;o<2;o++){
if(i<n&&dp[i]==dp[i+1]) dl[i][o]=dl[i+1][o]|turn(i+1,o);
else dl[i][o]=0;
}
int ans=dp[n];
if(chk()) ans--;
cout<<ans<<endl;
}
}
int c,t;
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>c>>t;
while(t--) tymxz::subt();
return 0;
}
:::
因为没有打标记,所以比较好写。