T1
根据倒数第二,三个部分分的提示,我们可以发现一个性质,如果两个连续的序列中间被间隔开,如 \(1,2,3,4,6,7,8,9\) 那这两个序列中选数操作互不影响,那这就比较好办了,一个长度为 \(n\) 连续序列最多可以选出 $ \lceil \frac{n}{2}\rceil$ 个数 , 那我们只需要维护连续的区间的个数以及长度,这道题就做完了。
那我们就可以考虑带权并查集 , 用并查集来维护连续的区间,每个新加入的数,要么单开一个区间,要么接在一个区间的一侧,要么连接两个区间,每当加入这个数之后,判断一下区间的能选的数的个数是否增加。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=6e5+107;
int n,a[N];
int num[N];
int vis[N],flag[N];
int fa[N];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int read()
{
int f=1,s=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return f*s;
}
int main()
{
n=read();
for(int i=1;i<N;i++) fa[i]=i;
int ans=0;
for(int i=1;i<=n;i++)
{
a[i]=read();
if(!vis[a[i]])
{
vis[a[i]]=1;
if(vis[a[i]-1]&&vis[a[i]+1])
{
int fx=find(a[i]);
int fy=find(a[i]-1);
int fz=find(a[i]+1);
if(fx!=fy)
{
fa[fx]=fy;
num[fy]=num[fx]+num[fy];
}
if(fy!=fz)
{
fa[fy]=fz;
if(num[fz]%2==0&&num[fy]%2==0) ans++;
num[fz]=num[fy]+num[fz]+1;
}
}
else if(vis[a[i]-1])
{
int fx=find(a[i]);
int fy=find(a[i]-1);
if(fx!=fy)
{
fa[fx]=fy;
num[fy]=num[fx]+num[fy]+1;
if(num[fy]%2==1) ans++;
}
}
else if(vis[a[i]+1])
{
int fx=find(a[i]);
int fy=find(a[i]+1);
if(fx!=fy)
{
fa[fx]=fy;
num[fy]=num[fx]+num[fy]+1;
if(num[fy]%2==1) ans++;
}
}
else
{
num[a[i]]++;
if(num[a[i]]%2==1) ans++;
}
}
printf("%d ",ans);
}
}
T2
容易发现一个性质:设目前有 \(n\) 个叶子节点,目前节点编号为 \(x\) ,那我们可以写出一个关于 \(x\) 的一次函数: $ f(x)=k_{n}x+b_{n}= (k_{\lceil\frac{n}{2}\rceil}(2x) + b_{\lceil\frac{n}{2}\rceil})+(k_{\lfloor\frac{n}{2}\rfloor}(2x+1)+b_{\lfloor\frac{n}{2}\rfloor})+x$
知道这个性质了,我们就可以递推并记忆化求解 \(n\) 个叶子节点时的 \(k\) 值和 \(b\) 值。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int n,x,y;
int read()
{
int f=1,s=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return f*s;
}
map<int,int> k,b;
void dp(int n)
{
if(k[n]) return ;
int lc=ceil(1.0*n/2),rc=floor(1.0*n/2);
dp(lc),dp(rc);
k[n]=(k[lc]+k[rc])*2%mod+1;
b[n]=(b[lc]+b[rc]+k[rc])%mod;
}
#define lson (rt<<1)
#define rson (rt<<1|1)
int query(int rt,int l,int r)
{
if(x<=l&&r<=y)
{
dp(r-l+1);
return (k[r-l+1]*rt%mod+b[r-l+1])%mod;
}
int ans=0;
int mid=(l+r)>>1;
if(x<=mid) (ans+=query(lson%mod,l,mid))%=mod;
if(y>mid) (ans+=query(rson%mod,mid+1,r))%=mod;
return ans;
}
signed main()
{
k[1]=1;
int T=read();
while(T--)
{
n=read(),x=read(),y=read();
printf("%lld\n",query(1,1,n));
}
}
T3
题目大意是要求解 \(min(max(a_{p}+a_{q},b_{p}+b_{q}))\) ,其中 \(p\) 代表法杖,\(q\) 代表咒语,比较套路的一种变换,假设 \(a_{p}+a_{q}> b_{p}+b_{q}\) ,那我们就可以将其变为 \(a_{p}-b_{p}>b_{q}-a_{q}\) ,这样就比较方便我们进行操作,我们令 \(u_{p}=a_{p}-b_{p} , v_{q}=b_{q}-a_{q}\),我们就以 \(u\) 和 \(v\) 为下标建一颗线段树。
这样我们就有一个比较好的性质,所有左区间的 \(u\) 都小于所有右区间的 \(v\) ,所有左区间的 \(v\) 都小于所有右区间的 \(u\) (这不废话嘛),我们在结合上面的判断可以得到整个区间的最小值可以由左区间的最小的 \(b_{p}\) 加上右区间最小的 \(b_{q}\) 得到,或者由左区间最小的 \(a_{q}\) 加上右区间最小的 \(a_{p}\) 得到,这样我们就可以用线段树来维护所有的信息了,好,这题完了。(记得 \(u\) 和 \(v\) 要加一个很大的正值,可能为负数)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2.5e5+107;
const int inf=1e9;
int q,t;
int read()
{
int f=1,s=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
return f*s;
}
multiset<int> s[N<<1][4];
#define lson (rt<<1)
#define rson (rt<<1|1)
struct lmy
{
int v[4],sum;
}tr[N<<4],tmp;
void pushup(int rt)
{
for(int i=0;i<4;i++) tr[rt].v[i]=min(tr[lson].v[i],tr[rson].v[i]);
tr[rt].sum=min(tr[lson].v[1]+tr[rson].v[0],tr[lson].v[2]+tr[rson].v[3]);
tr[rt].sum=min(tr[rt].sum,tr[lson].sum);
tr[rt].sum=min(tr[rt].sum,tr[rson].sum);
}
void update(int rt,int l,int r,int pos)
{
if(l==r)
{
tr[rt]=tmp;
if(tr[rt].v[0]+tr[rt].v[1]<=inf||tr[rt].v[2]+tr[rt].v[3]<=inf)
tr[rt].sum=min(tr[rt].v[0]+tr[rt].v[1],tr[rt].v[2]+tr[rt].v[3]);
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) update(lson,l,mid,pos);
else update(rson,mid+1,r,pos);
pushup(rt);
}
int ans=0;
int main()
{
q=read(),t=read();
for(int i=1;i<=N*4-1;i++)
{
for(int j=0;j<4;j++)
{
tr[i].v[j]=tmp.v[j]=inf;
}
tr[i].sum=tmp.sum=inf;
}
while(q--)
{
int op=read(),type=read(),a=read(),b=read();
if(t==1) a=ans^a,b=ans^b;
if(op==1)
{
if(type==0)
{
int x=a-b+N;
s[x][0].insert(a),s[x][2].insert(b);
for(int i=0;i<4;i++)
{
if(!s[x][i].empty()) tmp.v[i]=*(s[x][i].begin());
else tmp.v[i]=inf;
}
update(1,1,N<<1,x);
}
else
{
int x=b-a+N;
s[x][1].insert(a),s[x][3].insert(b);
for(int i=0;i<4;i++)
{
if(!s[x][i].empty()) tmp.v[i]=*(s[x][i].begin());
else tmp.v[i]=inf;
}
update(1,1,N<<1,x);
}
}
else
{
if(type==0)
{
int x=a-b+N;
if(!s[x][0].empty()&&!s[x][2].empty()&&s[x][0].find(a)!=s[x][0].end()&&s[x][2].find(b)!=s[x][0].end())
{
s[x][0].erase(a),s[x][2].erase(b);
for(int i=0;i<4;i++)
{
if(!s[x][i].empty()) tmp.v[i]=*(s[x][i].begin());
else tmp.v[i]=inf;
}
update(1,1,N<<1,x);
}
}
else
{
int x=b-a+N;
if(!s[x][1].empty()&&!s[x][3].empty()&&s[x][1].find(a)!=s[x][1].end()&&s[x][3].find(b)!=s[x][3].end())
{
s[x][1].erase(a),s[x][3].erase(b);
for(int i=0;i<4;i++)
{
if(!s[x][i].empty()) tmp.v[i]=*(s[x][i].begin());
else tmp.v[i]=inf;
}
update(1,1,N<<1,x);
}
}
}
ans=tr[1].sum;
if(ans>=N<<1) ans=0;
printf("%d\n",ans);
}
return 0;
}