根号分治
选定一个阈值,根据数据的大小对不同的数据进行不同的操作,平衡复杂度。
一般复杂度和根号有关,看到时限较大的可以考虑。
P3396 哈希冲突
给一个序列,支持单点修改,查询
不妨设一个阈值
如果
如果
总复杂度
经实验,
#include<bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define Rrep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template<class T>void read(T &x){
x=0;int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
x*=f;
}
typedef long long ll;
const int MAXN=150005;
int n,m,sz;
ll mem[390][390];
int a[MAXN];
int main(){
read(n),read(m);
rep(i,1,n)read(a[i]);
sz=pow(n,0.333);
int x,y;
ll res;
rep(i,1,n)
rep(j,1,sz)mem[j][i%j]+=a[i];
char o[5];
while(m--){
scanf("%s",o);
read(x),read(y);
if(o[0]=='A'){
if(x<=sz)printf("%lld\n",mem[x][y]);
else{
res=0;
for(int i=y;i<=n;i+=x)res+=a[i];
printf("%lld\n",res);
}
}
else{
rep(i,1,sz)mem[i][x%i]+=y-a[x];
a[x]=y;
}
}
return 0;
}
Time to Raid Cowavans
和上题差不多,但是无修改,询问
把询问离线,对于每个小模数暴力重构一次后缀和序列,大模数还是直接暴力。
#include<bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define Rrep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template<class T>void read(T &x){
x=0;int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
x*=f;
}
typedef long long ll;
const int MAXN=3e5+5;
int n,m,sz;
int a[MAXN];
ll s[MAXN],ans[MAXN];
struct qq{
int t,k,id;
bool operator<(const qq &T)const{
return k<T.k;
}
} q[MAXN];
void cal(int k){
memset(s,0,sizeof(s));
Rrep(i,n,1)
s[i]=((i+k<=n)?s[i+k]:0)+a[i];
}
int main(){
read(n);
sz=sqrt(n);
rep(i,1,n)read(a[i]);
read(m);
rep(i,1,m)read(q[i].t),read(q[i].k),q[i].id=i;
sort(q+1,q+m+1);
rep(i,1,m){
if(q[i].k!=q[i-1].k&&q[i].k<=sz)cal(q[i].k);
if(q[i].k<=sz)ans[q[i].id]=s[q[i].t];
else{
for(int j=q[i].t;j<=n;j+=q[i].k)ans[q[i].id]+=a[j];
}
}
rep(i,1,m)printf("%lld\n",ans[i]);
return 0;
}
You Are Given a Tree
先考虑如果
可以考虑一个类似于赛道修建的贪心,每个点维护从子树到这个点的最长路和次长路,如果加起来比
所以对于小于等于
当
这题卡常,可以把树重新建一遍,只保留父亲到儿子的边,快了10倍甚至9倍。
#include<bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define Rrep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template<class T>void read(T &x){
x=0;int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
x*=f;
}
const int MAXN=1e5+5;
int n,lim;
int ans[MAXN];
struct E{
int v,nxt;
} e[2][MAXN<<1];
int cnt[2],h[2][MAXN];
void ade(int u,int v,int o){
e[o][++cnt[o]].v=v;
e[o][cnt[o]].nxt=h[o][u];
h[o][u]=cnt[o];
}
void build(int u,int fa){
for(int i=h[0][u];i;i=e[0][i].nxt){
int v=e[0][i].v;
if(v==fa)continue;
ade(u,v,1);
build(v,u);
}
}
int res=0;
int dfs(int u,int k){
int mx=0,sec=0,v;
for(int i=h[1][u];i;i=e[1][i].nxt){
v=e[1][i].v;
int t=dfs(v,k);
if(t>=k){res++;return 0;}
if(t>mx)sec=mx,mx=t;
else if(t>sec)sec=t;
}
if(mx+sec+1>=k){res++;return 0;}
return mx+1;
}
int cal(int k){
res=0;
dfs(1,k);
return res;
}
int main(){
read(n);
lim=sqrt(n*log2(n));
int x,y;
rep(i,1,n-1)read(x),read(y),ade(x,y,0),ade(y,x,0);
build(1,0);
rep(i,1,lim)ans[i]=cal(i);
for(int i=1,pre=n+1;i<=n/lim+1;i++){
int l=1,r=pre;
while(l<r){
int mid=((l+r)>>1)+1;
if(cal(mid)>=i)l=mid;
else r=mid-1;
}
rep(j,l+1,pre-1)ans[j]=i-1;
pre=l+1;
}
rep(i,1,n)printf("%d\n",ans[i]);
return 0;
}
DZY Loves Strings
考虑到询问串的长度很小,我们先预处理出所有可能的询问串的出现次数,一共有
对于两个出现次数都
常数是别人的10倍甚至9倍。
#include<bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define Rrep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template<class T>void read(T &x){
x=0;int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
x*=f;
}
const int MAXN=5e4+5,MCNT=2e5+5,bs=27,MAXM=6e5,inf=1e9;
int n,q,lim;
string s;
int hs(int x,int y){
int res=0;
rep(i,x,y)res=res*bs+s[i]-'a'+1;
return res;
}
int hs2(string x){
int t=x.length();
int res=0;
rep(i,0,t-1)res=res*bs+x[i]-'a'+1;
return res;
}
int cnt[MAXM],lst[MAXM],len[MAXM];
vector<int> pl[MAXM],nam;
unordered_map<int,int> mp[MAXM];
int main(){
ios::sync_with_stdio(false);
cin>>s;
cin>>q;
n=s.length();
lim=sqrt(4.0*n);
s=' '+s;
int tt=0;
rep(i,1,n){
rep(k,1,4){
if(i+k-1>n)continue;
int t=hs(i,i+k-1);
cnt[t]++;
pl[t].push_back(i);
tt=max(tt,t);
len[t]=k;
}
}
rep(i,1,tt)
if(cnt[i]>lim)nam.push_back(i);
rep(i,1,n){
rep(k,1,4){
if(i+k-1>n)continue;
int hss=hs(i,i+k-1);
for(int j:nam){
if(!lst[j])continue;
int ed=max(lst[j]+len[j]-1,i+k-1);
int st=lst[j];
int x=hss,y=j;
if(x>y)swap(x,y);
mp[x][y]=min((!mp[x][y])?inf:mp[x][y],ed-st+1);
}
lst[hss]=i;
}
}
memset(lst,0,sizeof(lst));
Rrep(i,n,1){
rep(k,1,4){
if(i+k-1>n)continue;
int hss=hs(i,i+k-1);
for(int j:nam){
if(!lst[j])continue;
int ed=max(lst[j]+len[j]-1,i+k-1);
int st=i;
int x=hss,y=j;
if(x>y)swap(x,y);
mp[x][y]=min((!mp[x][y])?inf:mp[x][y],ed-st+1);
}
lst[hss]=i;
}
}
string a,b;
while(q--){
cin>>a>>b;
int hsa=hs2(a),hsb=hs2(b);
if(cnt[hsa]>cnt[hsb])swap(a,b),swap(hsa,hsb);
if((!cnt[hsa])||(!cnt[hsb])){cout<<"-1"<<endl;continue;}
if(a==b){cout<<len[hsa]<<endl;continue;}
if(cnt[hsb]>lim){
int x=hsa,y=hsb;
if(x>y)swap(x,y);
cout<<mp[x][y]<<endl;
continue;
}
else{
int res=inf;
for(int i:pl[hsa]){
int st,ed;
auto t=upper_bound(pl[hsb].begin(),pl[hsb].end(),i);
t--;
while(t<pl[hsb].end()&&*t<=i)t++;
if(t==pl[hsb].end()||*t>i)t--;
if(*t<=i){
st=*t,ed=max(*t+len[hsb]-1,i+len[hsa]-1);
res=min(res,ed-st+1);
}
t=upper_bound(pl[hsb].begin(),pl[hsb].end(),i);
while(t>=pl[hsb].begin()&&*t>=i)t--;
if(t<pl[hsb].begin()||*t<i)t++;
if(t==pl[hsb].end())t--;
if(*t>=i){
st=i,ed=max(*t+len[hsb]-1,i+len[hsa]-1);
res=min(res,ed-st+1);
}
}
cout<<res<<endl;
}
}
return 0;
}
破案了,原来第二种不用二分,也做一个扫描线就行了,我真傻,真的。
String Set Queries
考虑枚举已出现过的串的长度,在模板串中枚举每个长度的子串,用哈希判定。
复杂度为什么是对的呢?
考虑最坏情况,也就是已出现串的种类尽量多,那么每个串只有一个,从1开始单调递增,
这题卡哈希,用拉链法。
#include<bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define Rrep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template<class T>void read(T &x){
x=0;int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
x*=f;
}
typedef long long ll;
const int MAXN=3e5+5,mod=1e9+7,bs=29,MSZ=800,MOD=5003;
int m;
int pw[MAXN],id[MAXN],rev[MAXN],tot,has[MAXN];
struct HASH{
struct nd{
int ky,cnt;
};
vector<nd> a[MOD];
void ins(int x){
int t=x%MOD;
int sz=a[t].size();sz--;
rep(i,0,sz)
if(a[t][i].ky==x){a[t][i].cnt++;return;}
a[t].push_back((nd){x,1});
}
void del(int x){
int t=x%MOD;
int sz=a[t].size();sz--;
rep(i,0,sz)
if(a[t][i].ky==x){
a[t][i].cnt--;
if(!a[t][i].cnt)swap(a[t][i],a[t].back());
a[t].pop_back();
return;
}
a[t].push_back((nd){x,1});
}
int quy(int x){
int t=x%MOD;
int sz=a[t].size();sz--;
rep(i,0,sz)
if(a[t][i].ky==x)return a[t][i].cnt;
return 0;
}
} mp[MSZ];
int main(){
ios::sync_with_stdio(false);
cin>>m;
pw[0]=1;
rep(i,1,3e5)pw[i]=1ll*pw[i-1]*bs%mod;
int o,ls;
string s;
while(m--){
cin>>o>>s;
ls=s.length();
s=' '+s;
if(o==1){
int hs=0;
rep(i,1,ls)hs=(1ll*hs*bs+s[i]-'a'+1)%mod;
if(!id[ls])rev[id[ls]=++tot]=ls;
mp[id[ls]].ins(hs);
}
else if(o==2){
int hs=0;
rep(i,1,ls)hs=(1ll*hs*bs+s[i]-'a'+1)%mod;
mp[id[ls]].del(hs);
}
else{
rep(i,1,ls)has[i]=(1ll*has[i-1]*bs+s[i]-'a'+1)%mod;
int ans=0;
rep(i,1,tot){
int L=rev[i];
rep(j,rev[i],ls){
ans+=mp[i].quy(((ll)has[j]+mod-1ll*has[j-L]*pw[L]%mod)%mod);
}
}
cout<<ans<<endl;
fflush(stdout);
}
}
return 0;
}
P3645 [APIO2015] 雅加达的摩天楼
暴力建图,行不通。
我们考虑直接bfs,隐式建图。
复杂度为什么是对的呢?
对于
对于
我们可以用bitset判断每个点的每种距离是否拓展过。
#include<bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define Rrep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template<class T>void read(T &x){
x=0;int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
x*=f;
}
const int MAXN=3e4+5;
int n,m,b[MAXN],p[MAXN];
struct nd{
int x,stp,res;
};
vector<int> v[MAXN];
list<nd> q;
bitset<MAXN> vis[MAXN];
int main(){
read(n),read(m);
rep(i,0,m-1){
read(b[i]),read(p[i]);
v[b[i]].push_back(p[i]);
}
q.push_back((nd){b[0],p[0],0});
nd u;
while(!q.empty()){
u=q.front();
q.pop_front();
if(u.x==b[1]){printf("%d\n",u.res);return 0;}
if(vis[u.x][u.stp])continue;
vis[u.x][u.stp]=1;
if(u.x-u.stp>=0)q.push_back((nd){u.x-u.stp,u.stp,u.res+1});
if(u.x+u.stp<=n-1)q.push_back((nd){u.x+u.stp,u.stp,u.res+1});
for(int i:v[u.x])q.push_front((nd){u.x,i,u.res});
}
puts("-1");
return 0;
}
P5309 [Ynoi2011] 初始化
先考虑修改操作。
对于每个
对于每个
然后把序列分块维护,答案就是原序列的和加上每种模数的懒标记的和。
第二种对于每个模数在序列中会呈现一个周期,然后维护表中每个每一行的前缀、后缀和就行了,修改的时候暴力更新前缀后缀和。
#include<bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define Rrep(a,b,c) for(int a=b;a>=c;a--)
#define admd(a,b) a=(a+b>mod)?(a+b-mod):(a+b)
using namespace std;
template<class T>void read(T &x){
x=0;int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
x*=f;
}
const int MAXN=2e5+5,mod=1e9+7,MSZ=500;
const int lim=150;
int n,m,sz,num,a[MAXN];
int l[MSZ],r[MSZ],pos[MAXN],s[MSZ],pre[MSZ][MSZ],suf[MSZ][MSZ];
void build(){
sz=sqrt(n);
num=ceil(n*1.0/sz);
rep(i,1,num)l[i]=(i-1)*sz+1,r[i]=i*sz;
r[num]=n;
rep(i,1,n)read(a[i]),pos[i]=(i-1)/sz+1,admd(s[pos[i]],a[i]);
}
int quy(int nl,int nr){
int res=0;
if(pos[nl]==pos[nr])
rep(i,nl,nr)admd(res,a[i]);
else{
rep(i,nl,r[pos[nl]])admd(res,a[i]);
rep(i,l[pos[nr]],nr)admd(res,a[i]);
rep(i,pos[nl]+1,pos[nr]-1)admd(res,s[i]);
}
int ed=min(lim,n);
rep(i,1,ed){
int t1=(nl-1)/i+1,t2=(nr-1)/i+1;
if(t1==t2){
admd(res,pre[i][nr-i*(t2-1)]);
admd(res,mod-pre[i][nl-1-i*(t1-1)]);
}
else{
admd(res,1ll*pre[i][i]*(t2-t1-1)%mod);
admd(res,pre[i][nr-i*(t2-1)]);
admd(res,suf[i][nl-i*(t1-1)]);
}
}
return res;
}
int main(){
read(n),read(m);
build();
int o,x,y,z;
while(m--){
read(o),read(x),read(y);
if(o==1){
read(z);
if(x<=lim){
rep(i,y,x)admd(pre[x][i],z);
Rrep(i,y,1)admd(suf[x][i],z);
}
else
for(int i=y;i<=n;i+=x)admd(a[i],z),admd(s[pos[i]],z);
}
else printf("%d\n",quy(x,y));
}
return 0;
}
P5901 [IOI2009] regions
先考虑两种暴力,第一种是从根开始dfs,求得
第二种是倒着dfs。
如果
如果
在dfs的时候我们维护一个桶,利用更新前后的差值计算答案。
#include<bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define Rrep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template<class T>void read(T &x){
x=0;int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
x*=f;
}
const int MAXN=2e5+5,MAXR=2.5e4+5;
int n,r,Q,lim;
int h[MAXN],s[MAXR],ans[MAXN],bkt[MAXN];
struct E{
int v,nxt;
} e[MAXN];
int ecnt,hd[MAXN];
void ade(int u,int v){
e[++ecnt].v=v;
e[ecnt].nxt=hd[u];
hd[u]=ecnt;
}
vector<pair<int,int> > q1[MAXN],q2[MAXN];
void dfs1(int u){
for(auto i:q1[h[u]])ans[i.first]-=bkt[i.second];
bkt[h[u]]++;
for(int i=hd[u];i;i=e[i].nxt)dfs1(e[i].v);
for(auto i:q1[h[u]])ans[i.first]+=bkt[i.second];
}
void dfs2(int u){
bkt[h[u]]++;
for(auto i:q2[h[u]])ans[i.first]+=bkt[i.second];
for(int i=hd[u];i;i=e[i].nxt)dfs2(e[i].v);
bkt[h[u]]--;
}
int main(){
read(n),read(r),read(Q);
lim=sqrt(n);
read(h[1]);
s[h[1]]++;
int x;
rep(i,2,n){
read(x);
ade(x,i);
read(h[i]);
s[h[i]]++;
}
int r1,r2;
rep(i,1,Q){
read(r1),read(r2);
if(s[r2]>=lim)q1[r1].push_back(make_pair(i,r2));
else q2[r2].push_back(make_pair(i,r1));
}
dfs1(1);
memset(bkt,0,sizeof(bkt));
dfs2(1);
rep(i,1,Q)printf("%d\n",ans[i]);
return 0;
}