A. 不相邻集合
可以发现,一个数只有在第一次出现才会做贡献,对于一个连续数段 \(1,2,3...n\) ,它最多提供 \(\lceil \frac{n}{2} \rceil\)的贡献,所以只需要维护
极长连续段即可
点击查看代码
#include<bits/stdc++.h>
const int maxn=3e5+10;
using namespace std;
int n,a[maxn],fa[500005],size[500005],ans;
bool vis[500005];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int mo(int x){return (size[x]+1)>>1;}
void merge(int x,int y)
{
int a=find(x),b=find(y);
if(a==b) return ;
fa[b]=a;
ans-=mo(a)+mo(b);
size[a]+=size[b];
ans+=mo(a);
}
int main()
{
// freopen("set1.in","r",stdin);
// freopen("T.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=500001;i++) fa[i]=i,size[i]=1;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(!vis[a[i]])ans++;
vis[a[i]]=1;
if(vis[a[i]-1])
{
// cout<<find(a[i]-1)<<" "<<find(a[i])<<endl;
merge(a[i]-1,a[i]);
}
if(vis[a[i]+1])
{
merge(a[i],a[i]+1);
}
cout<<ans<<" ";
}
return 0;
}
/*
*/
B. 线段树
设 \(f(n,x)\) 为有 \(n\) 个节点,根标号为 \(x\) 的线段树的节点标号和,可以发现其是一个确定的一次函数
因为 \(f(n,x)=f(\lceil \frac{n}{2} \rceil,2x)+f(\lfloor \frac{n}{2} \rfloor,2x+1)+x\),可以推出 \(k_n=2(k_{\lceil \frac{n}{2} \rceil}+k_{\lfloor \frac{n}{2} \rfloor})+1\),\(b_n=(b_{\lceil \frac{n}{2} \rceil}+b_{\lfloor \frac{n}{2} \rfloor}+k_{\lfloor \frac{n}{2} \rfloor})\)
每次查到一个区间属于 \(x\)~\(y\) ,没查过则处理一下 \(k_n\) 和 \(b_n\) ,查过则直接返回,单次复杂度 \(O(log_n)\)
点击查看代码
#include<bits/stdc++.h>
#define int long long
const int mod=1e9+7;
const int maxn=2e5+10;
using namespace std;
int t,n,x,y;
map<int ,int >k,b;
void lsx(int x)
{
if(x==1) return ;
if(k[x]&&b[x]) return ;
lsx(floor(1.0*x/2));
k[x]+=2*k[floor(1.0*x/2)];
b[x]+=b[floor(1.0*x/2)]+k[floor(1.0*x/2)];
k[x]%=mod;
b[x]%=mod;
lsx(ceil(1.0*x/2));
k[x]+=2*k[ceil(1.0*x/2)]+1;
b[x]+=b[ceil(1.0*x/2)];
k[x]%=mod;
b[x]%=mod;
}
int solve(int id,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
{
lsx(r-l+1);
return (k[r-l+1]*id%mod+b[r-l+1])%mod;
}
int mid=l+r>>1,tem=0;
if(mid>=x) tem=(tem+solve((id<<1)%mod,l,mid,x,y))%mod;
if(mid<y) tem=(tem+solve((id<<1|1)%mod,mid+1,r,x,y))%mod;
return tem;
}
signed main()
{
// freopen("T.in","r",stdin);
// freopen("T.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
k[1]=1,b[1]=0;
cin>>t;
while(t--)
{
cin>>n>>x>>y;
cout<<solve(1,1,n,x,y)<<'\n';
}
return 0;
}
/*
*/
C. 魔法师
对于魔杖 \(p\) 和魔法 \(q\) ,它们的魔法是 \(\max(a_p+a_q,b_p+b_q)\),先分讨简化,假设 \(a_p+a_q<b_p+b_q\),则有 \(a_p-b_p<b_q-a_q\)
记 \(u_p=a_p-b_q,v_q=b_q-a_q\),则魔力的大小则取决于 \(u_p\) 和 \(v_q\) 的大小,把所有魔杖和魔法以其 \(u_p/v_q\) 为下标存到一棵权值
线段树上,找右区间最小的 \(b_p\) 和左区间最小的 \(b_q\),和找右区间最小的 \(a_p\) 和左区间最小的 \(a_q\)即可
点击查看代码
#include<bits/stdc++.h>
#define lid id<<1
#define rid id<<1|1
const int maxn=25e4+10;
const int mm=25e4;
const int inf=0x7f7f7f;
using namespace std;
int Q,T,ans,last,cnt1,cnt2;
struct lsx
{
int l,r,a[4],ans;
}m[maxn<<3],s;
multiset<int >q[maxn<<1][4];
void build(int id,int l,int r)
{
m[id].l=l,m[id].r=r;
m[id].ans=m[id].a[0]=m[id].a[1]=m[id].a[2]=m[id].a[3]=inf;
if(l==r) return ;
int mid=l+r>>1;
build(lid,l,mid);
build(rid,mid+1,r);
}
void up(int id)
{
for(int i=0;i<4;i++) m[id].a[i]=min(m[lid].a[i],m[rid].a[i]);
m[id].ans=min(m[lid].a[3]+m[rid].a[1],m[lid].a[0]+m[rid].a[2]);
m[id].ans=min(m[id].ans,min(m[lid].ans,m[rid].ans));
}
void update(int id,int x)
{
int l=m[id].l,r=m[id].r;
if(l==r)
{
m[id]=s;
if(m[id].a[0]+m[id].a[2]<=inf||m[id].a[1]+m[id].a[3]<=inf)
m[id].ans=min(m[id].a[0]+m[id].a[2],m[id].a[1]+m[id].a[3]);
return ;
}
int mid=l+r>>1;
if(x<=mid) update(lid,x);
else update(rid,x);
up(id);
}
int main()
{
// freopen("T.in","r",stdin);
// freopen("T.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>Q>>T;
build(1,1,mm*2);
s.a[0]=s.a[1]=s.a[2]=s.a[3]=s.ans=inf;
while(Q--)
{
int op,t,x,y;
cin>>op>>t>>x>>y;
if(T) x^=last,y^=last;
int pos=t?y-x+mm:x-y+mm;
if(op==1)
{
if(t)
{
q[pos][0].insert(x),q[pos][1].insert(y);
for(int i=0;i<4;i++)
{
if(q[pos][i].size()) s.a[i]=*q[pos][i].begin();
else s.a[i]=inf;
}
// cout<<s.a[0]<<" "<<s.a[1]<<" "<<s.a[2]<<" "<<s.a[3]<<endl;
update(1,pos);
}
else
{
q[pos][2].insert(x),q[pos][3].insert(y);
for(int i=0;i<4;i++)
{
if(q[pos][i].size()) s.a[i]=*q[pos][i].begin();
else s.a[i]=inf;
}
// cout<<s.a[0]<<" "<<s.a[1]<<" "<<s.a[2]<<" "<<s.a[3]<<endl;
update(1,pos);
}
}
else
{
if(t)
{
if(q[pos][0].size()&&q[pos][1].size()&&q[pos][0].find(x)!=q[pos][0].end()&&q[pos][1].find(y)!=q[pos][1].end())
{
q[pos][0].erase(q[pos][0].find(x));
q[pos][1].erase(q[pos][1].find(y));
for(int i=0;i<4;i++)
{
if(q[pos][i].size()) s.a[i]=*q[pos][i].begin();
else s.a[i]=inf;
}
update(1,pos);
}
}
else
{
if(q[pos][2].size()&&q[pos][3].size()&&q[pos][2].find(x)!=q[pos][2].end()&&q[pos][3].find(y)!=q[pos][3].end())
{
q[pos][2].erase(q[pos][2].find(x));
q[pos][3].erase(q[pos][3].find(y));
for(int i=0;i<4;i++)
{
if(q[pos][i].size()) s.a[i]=*q[pos][i].begin();
else s.a[i]=inf;
}
update(1,pos);
}
}
}
cout<<(m[1].ans>=inf?0:m[1].ans)<<'\n';
last=(m[1].ans>=inf?0:m[1].ans);
}
return 0;
}
/*
*/