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;
}