Ynoi2011题解
d1t1 初始化
题意
一个长度为 \(n\) 的序列,\(q\) 次操作。
- 询问 \([l,r]\) 的区间和。
- 将所有 \(\bmod x=y\) 的下标位置加 \(z\)。
\(n,q\leq 2\times 10^5\)。
题解
考虑根号分治。
- \(x>\sqrt n\),修改位置不超过 \(\sqrt n\),暴力修改,利用分块维护来平衡复杂度。
- \(x<\sqrt n\),对于一个询问,把序列按照 \(x\) 分块,中间是一些整块,两边是一个前缀和后缀和的形式,直接用 \(tag(x,y)\) 表示 \((x,y)\) 的操作中 \(z\) 的总和是多少,然后每次暴力算一下 \(x\) 关于 \(y\) 的前缀和后缀和即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
typedef double db;
# define chkmax(a,b) a=max(a,b)
# define chkmin(a,b) a=min(a,b)
# define PII pair<int,int>
# define mkp make_pair
const int N=2e5+5;
const int M=505;
const int mod=1e9+7;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,m,B,bl;
int a[N];
int pos[N],L[N],R[N];
int val[N],sum[M];
int g[M][M];
# define tas(x,y) (x=x+y>=mod?x+y-mod:x+y)
int main()
{
read(n),read(m);
B=sqrt(n)+1;
Rep(i,1,n)read(a[i]);
bl=(n-1)/B+1;
Rep(i,1,bl)L[i]=(i-1)*B+1,R[i]=i*B;
R[bl]=n;
Rep(i,1,n)pos[i]=(i-1)/B+1;
Rep(i,1,n)val[i]=a[i],tas(sum[pos[i]],a[i]);
while(m--){
int opt,x,y,z;
read(opt),read(x),read(y);
if(opt==1){
read(z);
if(x>B)for(;y<=n;y+=x)tas(val[y],z),tas(sum[pos[y]],z);
else Rep(i,y,x)tas(g[x][i],z);
}
else{
ll ans=0;
if(pos[x]==pos[y])Rep(i,x,y)ans+=val[i];
else{
for(int i=pos[x]+1;i<pos[y];i++)ans+=sum[i];
Rep(i,x,R[pos[x]])ans+=val[i];
Rep(i,L[pos[y]],y)ans+=val[i];
}
Rep(i,1,B){
int pl=(x-1)/i;
int pr=(y-1)/i;
if(pl==pr)ans+=g[i][(y-1)%i+1]-g[i][(x-1)%i];
else{
ans+=1ll*(pr-pl-1)*g[i][i]%mod;
ans+=g[i][i]-g[i][(x-1)%i];
ans+=g[i][(y-1)%i+1];
}
}
ans%=mod;
printf("%lld\n",tas(ans,mod));
}
}
return 0;
}
d1t2 遥远的过去
题意
有两个所有字符各不相同的字符串 \(A,B,|A|=n,|B|=m\),定义两个字符串匹配当且仅当他们长度相同,并且他们的字符离散化之后相同。现在 \(q\) 次修改 \(B\) 中的一个位置,询问修改后的 \(B\) 作为子串在 \(A\) 中的匹配次数。
\(n,m,q\leq 10^5,m\leq n\)
题解
考虑一个哈希:\(hash(S)=\sum_{i=1}^n base^ipos_i\),其中 \(pos_i\) 为 \(S\) 中第 \(i\) 大的字符所在的位置。
那么先预处理出 \(A\) 中所有的长度为 \(m\) 的子串的哈希值,扔到哈希表里,然后利用平衡树快速维护每次修改之后 \(B\) 的哈希值
点击查看代码
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
const int N=1e5+5;
const int base=1145141;
# define ll long long
# define db double
# define ull unsigned long long
# define uint unsigned int
# define chkmax(a,b) a=max(a,b)
# define chkmin(a,b) a=min(a,b)
# define mkp make_pair
# define PII pair<int,int>
# define PLL pair<ll,ll>
# define PIL pair<int,ll>
# define PLI pair<ll,int>
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,m,q;
int a[N],b[N];
int siz[N],son[N][2],treap[N],val[N];
int rt;
ull ha[N],poww[N];
mt19937 rnd(time(0));
struct myhash{
const int mod=19260817;
int head[20000005],cnt;
struct Edge{
ull to;
int next,cnt;
}e[N<<2];
void ins(ull x){
int y=x%mod;
for(int i=head[y];i;i=e[i].next)
if(e[i].to==x){e[i].cnt++;return;}
e[++cnt]=(Edge){x,head[y],1},head[y]=cnt;
}
int ask(ull x){
int y=x%mod;
for(int i=head[y];i;i=e[i].next)
if(e[i].to==x)return e[i].cnt;
return 0;
}
}var;
void update(int x){
siz[x]=siz[son[x][0]]+siz[son[x][1]]+1;
ha[x]=ha[son[x][0]]+x*poww[siz[son[x][0]]]+ha[son[x][1]]*poww[siz[son[x][0]]+1];
}
int merge(int u,int v){
if(!u||!v)return u|v;
int rt;
if(treap[u]<treap[v])son[rt=u][1]=merge(son[u][1],v);
else son[rt=v][0]=merge(u,son[v][0]);
return update(rt),rt;
}
void split(int o,int &u,int &v,int k){
if(!o){u=v=0;return;}
if(val[o]<=k)split(son[u=o][1],son[o][1],v,k);
else split(son[v=o][0],u,son[o][0],k);
update(o);
}
void insert(int x){
int lft,rht;
split(rt,lft,rht,val[x]);
update(x);
rt=merge(merge(lft,x),rht);
}
void erase(int x){
int lft,mid,rht;
split(rt,lft,rht,val[x]);
split(lft,lft,mid,val[x]-1);
son[x][0]=son[x][1]=0;
val[x]=ha[x]=0;
rt=merge(lft,rht);
}
int main()
{
# ifndef ONLINE_JUDGE
freopen("testdata.in","r",stdin);
//freopen("test1.out","w",stdout);
# endif
read(n),read(m),read(q);
Rep(i,1,n)read(a[i]);
Rep(i,1,m)read(b[i]);
poww[0]=1;
Rep(i,1,n)poww[i]=poww[i-1]*base,treap[i]=rnd();
ull d=0;
Rep(i,1,m)val[i]=a[i],insert(i),d+=poww[i-1];
Rep(i,m,n){
var.ins(ha[rt]-d*(i-m));
erase(i-m+1);
if(i==n)break;
val[i+1]=a[i+1];
insert(i+1);
}
rt=0;
memset(son,0,sizeof(son));
memset(ha,0,sizeof(ha));
Rep(i,1,m)val[i]=b[i],insert(i);
while(q--){
int x,y;
read(x),read(y);
erase(x);
val[x]=y;
insert(x);
printf("%d\n",var.ask(ha[rt]));
}
return 0;
}
d1t3 成都七中
题意
给定一棵树,每个点有一个颜色,\(q\) 次查询,每次询问编号在 \([l,r]\) 中的节点保留,\(x\) 所在的连通块的颜色种类数
\(n,q\leq 10^5\)
题解
注意到一个联通块中一定有一个点,使得这个连通块中所有的点都在点分树上以这个点为根的子树中。
那么就可以把 \([l,r,x]\) 挂在这个点上,然后遍历点分树上的每一个点 \(p\),对于这个点分树子树中的每个点 \(q\) 求出他在原树上到 \(p\) 的路径上经过的节点编号的最小值和最大值 \(mn_q,mx_q\),那么一次询问相当于数矩形内的颜色数,直接按 \(mx\) 和 \(r\) 排序,简单扫描线即可。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
const int N=1e5+5;
# define ll long long
# define db double
# define ull unsigned long long
# define uint unsigned int
# define chkmax(a,b) a=max(a,b)
# define chkmin(a,b) a=min(a,b)
# define mkp make_pair
# define PII pair<int,int>
# define PLL pair<ll,ll>
# define PIL pair<int,ll>
# define PLI pair<ll,int>
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,m;
int a[N];
int head[N],cnt;
int siz[N],mxsiz[N];
int p[N],totp;
int bit[N],mxl[N];
int out[N];
bool vis[N];
struct Edge{
int to,next;
}e[N<<1];
void add(int x,int y){
e[++cnt]=(Edge){y,head[x]},head[x]=cnt;
}
struct misaka{
int l,r,id;
};
bool cmp(misaka x,misaka y){
if(x.r!=y.r)return x.r<y.r;
else return x.l<y.l;
}
vector<misaka> son[N],T[N],Q[N];
void upd(int l,int r,int x){
for(int o=l;o<=n;o+=o&-o)bit[o]+=x;
for(int o=r+1;o<=n;o+=o&-o)bit[o]-=x;
}
int ask(int o){
int res=0;
for(;o;o-=o&-o)res+=bit[o];
return res;
}
void clear(int o){
for(;o<=n;o+=o&-o)bit[o]=0;
}
void dfs1(int u,int fa){
p[++totp]=u;
siz[u]=1,mxsiz[u]=0;
RepG(i,u){
int v=e[i].to;
if(v==fa||vis[v])continue;
dfs1(v,u);
siz[u]+=siz[v];
chkmax(mxsiz[u],siz[v]);
}
}
void dfs2(int u,int fa,int l,int r,int ff){
chkmin(l,u),chkmax(r,u);
son[ff].push_back((misaka){l,r,u});
T[u].push_back((misaka){l,r,ff});
RepG(i,u){
int v=e[i].to;
if(v==fa||vis[v])continue;
dfs2(v,u,l,r,ff);
}
}
void solve(int u,int ff){
totp=0;
dfs1(u,0);
int rt=p[1];
Rep(i,2,totp)if(max(mxsiz[p[i]],totp-siz[p[i]])<max(mxsiz[rt],totp-siz[rt]))rt=p[i];
dfs2(rt,0,rt,rt,rt);
vis[rt]=true;
RepG(i,rt){
int v=e[i].to;
if(vis[v])continue;
solve(v,rt);
}
}
int main()
{
# ifndef ONLINE_JUDGE
freopen("testdata.in","r",stdin);
//freopen("test1.out","w",stdout);
# endif
memset(head,-1,sizeof(head));
read(n),read(m);
Rep(i,1,n)read(a[i]);
Rep(i,1,n-1){
int x,y;
read(x),read(y);
add(x,y),add(y,x);
}
solve(1,0);
Rep(i,1,n)reverse(T[i].begin(),T[i].end());
Rep(i,1,m){
int l,r,x;
read(l),read(r),read(x);
int now=x;
for(int j=0;j<T[x].size();j++)
if(T[x][j].l>=l&&T[x][j].r<=r)now=T[x][j].id;
Q[now].push_back((misaka){l,r,i});
}
Rep(i,1,n){
sort(Q[i].begin(),Q[i].end(),cmp);
sort(son[i].begin(),son[i].end(),cmp);
int now=0;
for(int j=0;j<Q[i].size();j++){
while(now<son[i].size()&&son[i][now].r<=Q[i][j].r){
if(mxl[a[son[i][now].id]]<son[i][now].l)
upd(mxl[a[son[i][now].id]]+1,son[i][now].l,1),mxl[a[son[i][now].id]]=son[i][now].l;
now++;
}
out[Q[i][j].id]=ask(Q[i][j].l);
}
now=0;
clear(1);
for(int j=0;j<son[i].size();j++)mxl[a[son[i][j].id]]=0,clear(son[i][j].l),clear(son[i][j].l+1);
}
Rep(i,1,m)printf("%d\n",out[i]);
return 0;
}
d2t1 竞赛实验班
题意
维护一个长度为 \(n\) 的序列,\(m\) 次操作
- 在末尾插入 \(x\)
- 询问区间和
- 全局异或 \(x\)
- 将数组 \(A\) 排序
\(n,m\leq 10^5\)
题解
首先不考虑最后一个操作,那么只需要 log 棵线段树维护每一位的值。
加上最后一个之后,我们可以发现一定是前面一段是有序的之后异或上一个 \(x\),后面一段是无序的。
后面一段就维护每一位的1的个数,前面部分拿一棵 trie 树维护,每个点维护其子树中每一位的1的个数。
异或直接记一个全局 tag,扫描 trie 树的时候根据 tag 看是先进左子树还是右子树。
每次排序就暴力把后半部分无序的暴力扔到 trie 里面即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
const int N=2e5+5;
const int M=N*30;
# define ll long long
# define db double
# define ull unsigned long long
# define uint unsigned int
# define chkmax(a,b) a=max(a,b)
# define chkmin(a,b) a=min(a,b)
# define mkp make_pair
# define PII pair<int,int>
# define PLL pair<ll,ll>
# define PIL pair<int,ll>
# define PLI pair<ll,int>
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,q,now,tag,trietag;
int a[N],b[N][30];
int trie[M][2],siz[M];
int cnt[M][30],tot;
int res[30];
void insert(int x){
int u=0;
_Rep(i,29,0){
int j=x>>i&1;
if(!trie[u][j])trie[u][j]=++tot;
u=trie[u][j],siz[u]++;
Rep(j,0,29)cnt[u][j]+=x>>j&1;
}
}
void query(int u,int bit,int k,int opt){
if(bit==-1){
Rep(j,0,29)if(cnt[u][j])res[j]+=opt*k;
return;
}
int i=trietag>>bit&1;
if(siz[trie[u][i]]<=k){
Rep(j,0,29)res[j]+=opt*cnt[trie[u][i]][j];
query(trie[u][i^1],bit-1,k-siz[trie[u][i]],opt);
}
else query(trie[u][i],bit-1,k,opt);
}
int main()
{
# ifndef ONLINE_JUDGE
freopen("testdata.in","r",stdin);
//freopen("test1.out","w",stdout);
# endif
read(n);
Rep(i,1,n)read(a[i]);
Rep(i,1,n)Rep(j,0,29)b[i][j]=b[i-1][j]+(a[i]>>j&1);
read(q);
while(q--){
int opt,x,y;
read(opt);
if(opt==1){
read(x),x^=tag;
a[++n]=x;
Rep(j,0,29)b[n][j]=b[n-1][j]+(x>>j&1);
}
if(opt==2){
Rep(j,0,29)res[j]=0;
read(x),read(y);
if(y<=now)query(0,29,y,1),query(0,29,x-1,-1);
else if(x<=now){
query(0,29,now,1),query(0,29,x-1,-1);
Rep(j,0,29)res[j]+=b[y][j]-b[now][j];
}
else Rep(j,0,29)res[j]=b[y][j]-b[x-1][j];
ll ans=0;
Rep(j,0,29)
if(tag>>j&1)ans+=(y-x+1-res[j])*(1ll<<j);
else ans+=res[j]*(1ll<<j);
printf("%lld\n",ans);
}
if(opt==3)read(x),tag^=x;
if(opt==4){
trietag=tag;
Rep(i,now+1,n)insert(a[i]);
now=n;
}
}
return 0;
}
d2t2 WBLT
题意
不好说
题解
对于 \(b\geq B\),莫队维护区间 bitset,然后每 \(b\) 个分裂出一块,然后 and 一下,复杂度是 \(O(m\times \dfrac{n}{b}\dfrac{b}{w})=O(\dfrac{nm}{w})\)
对于每个 \(b<B\) 分开处理,对于每一个 \(\bmod b\) 的余数开一个 bitset,每次 findfirst0,复杂度 \(O(mb\times \dfrac{n}{bw})=O(\dfrac{nm}{w})\)
\(B=64\) 或者 \(128\) 比较优秀。
为什么要分两种是因为实际上除法是向上取整,这两种做法向上取整的位置不太一样。
需要手写 bitset。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
const int N=1e5+5;
const int sak=64;
# define ll long long
# define db double
# define ull unsigned long long
# define uint unsigned int
# define chkmax(a,b) a=max(a,b)
# define chkmin(a,b) a=min(a,b)
# define mkp make_pair
# define PII pair<int,int>
# define PLL pair<ll,ll>
# define PIL pair<int,ll>
# define PLI pair<ll,int>
const int Mxdt = (1<<16);inline char pc(char ch,bool bj){ static char buf[Mxdt],*p1=buf,*p2=buf+Mxdt; return ((bj)||(*p1++=ch)&&p1==p2)&&fwrite(p1=buf,1,p1-buf,stdout),0;}void print(int x){ if(x>9)print(x/10); pc(x%10^48,false);}inline void printnum(int x,int ch){ if(x < 0)pc('-',false),x=-x; print(x),pc(ch,false);}
char gc()
{
static char buf[1<<16],*S,*T;
if(T==S)
{
T=(S=buf)+fread(buf,1,1<<16,stdin);
if(T==S) return EOF;
}
return *S++;
}
#define getchar gc
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,m,B,BB;
int a[N];
int pos[N];
int cnt[N];
int out[N];
ull low[64];
struct misaka{
int l,r,b,id;
bool operator < (const misaka &cmp)const{
if(pos[l]!=pos[cmp.l])return pos[l]<pos[cmp.l];
else if(pos[l]&1)return r<cmp.r;
else return r>cmp.r;
}
}q1[N];
vector<misaka> q2[sak];
int totq;
struct Bitset{
vector<ull> a;
int siz;
void clear(){for(int i=0;i<siz;i++)a[i]=0;}
void init(int len){a.resize(len/64+1),siz=a.size();clear();}
void set(int x){a[x>>6]|=(1ull<<(x&63));}
void reset(int x){a[x>>6]&=~(1ull<<(x&63));}
void flip(){for(int i=0;i<siz;i++)a[i]=~a[i];}
bool empty(){for(int i=0;i<siz;i++)if(a[i])return false;return true;}
int findfirst0(){for(int i=0;i<siz;i++)if(~a[i])for(int j=0;j<64;j++)if(a[i]>>j&1^1)return i*64+j;}
void operator &=(const Bitset &x){for(int i=0;i<siz;i++)a[i]&=x.a[i];}
};
Bitset S,Sp[1600],res;
void split(int B){
int all=100001/B+2;
for(int i=0;i<=all;i++)Sp[i].init(B);
int k=Sp[0].siz-1,b=B&63,delta=0,now=0,id=0;
for(int i=0;i<S.siz;i++,now++)
if(now==k){
if(!b)i--;
else if(delta+b<=63)Sp[id].a[now]=(S.a[i]>>delta)&low[b-1],i--;
else if(delta+b==64)Sp[id].a[now]=S.a[i]>>delta;
else if(i!=S.siz-1)Sp[id].a[now]=(S.a[i]>>delta)^((S.a[i+1]&low[delta+b-65])<<64-delta);
else Sp[id].a[now]=S.a[i]>>delta;
delta=(delta+b)%64,id++,now=-1;
}
else{
if(delta==0)Sp[id].a[now]=S.a[i];
else if(i!=S.siz-1)Sp[id].a[now]=(S.a[i]>>delta)^((S.a[i+1]&low[delta-1])<<64-delta);
else Sp[id].a[now]=S.a[i]>>delta;
}
}
void add1(int x){cnt[a[x]]++;if(cnt[a[x]]==1)S.set(a[x]);}
void del1(int x){cnt[a[x]]--;if(cnt[a[x]]==0)S.reset(a[x]);}
void add2(int x){cnt[a[x]]++;if(cnt[a[x]]==1)Sp[a[x]%BB].set(a[x]/BB);}
void del2(int x){cnt[a[x]]--;if(cnt[a[x]]==0)Sp[a[x]%BB].reset(a[x]/BB);}
int main()
{
# ifndef ONLINE_JUDGE
freopen("testdata.in","r",stdin);
//freopen("test1.out","w",stdout);
# endif
low[0]=1;
for(int i=1;i<63;i++)low[i]=low[i-1]<<1|1;
read(n);
Rep(i,1,n)read(a[i]);
read(m);
Rep(i,1,m){
int l,r,b;
read(l),read(r),read(b);
if(b<sak)q2[b].push_back((misaka){l,r,b,i});
else q1[++totq]=(misaka){l,r,b,i};
}
B=n/sqrt(totq+1)+1;
Rep(i,1,n)pos[i]=(i-1)/B+1;
sort(q1+1,q1+totq+1);
S.init(100002);
for(int i=1,l=1,r=0;i<=totq;i++){
while(l>q1[i].l)add1(--l);
while(r<q1[i].r)add1(++r);
while(l<q1[i].l)del1(l++);
while(r>q1[i].r)del1(r--);
split(q1[i].b);
res.init(q1[i].b),res.flip();
for(int j=0;;j++){
res&=Sp[j];
if(res.empty()){out[q1[i].id]=j;break;}
}
}
for(int b=1;b<sak;b++){
if(!q2[b].size())continue;BB=b;
memset(cnt,0,sizeof(cnt));
B=n/sqrt((int)q2[b].size())+1;
Rep(i,1,n)pos[i]=(i-1)/B+1;
sort(q2[b].begin(),q2[b].end());
for(int j=0;j<b;j++)Sp[j].init(100002/b+1);
for(int i=0,l=1,r=0;i<q2[b].size();i++){
while(l>q2[b][i].l)add2(--l);
while(r<q2[b][i].r)add2(++r);
while(l<q2[b][i].l)del2(l++);
while(r>q2[b][i].r)del2(r--);
for(int j=0;j<b;j++)chkmax(out[q2[b][i].id],Sp[j].findfirst0());
}
}
Rep(i,1,m)printf("%d\n",out[i]);
return 0;
}