浅谈整除分块(复习)
复习整除分块
经典例子:
求Σ(n/i),n<=1e14,()为向下取整
考虑直接暴力肯定不行,但发现其中有很多数是一样的
引进整除分块:
右端点为n/(n/l):表示n/l的值n中有多少个
左端点为上一个r+1
复杂度为根号n
code:
inline void init (int ans=0) {
for(int l=1,r,len;l<=n;l=r+1) {
r=n/(n/l),len=r-l+1;
ans+=len*(n/l);
}
}
当然整除分块不仅仅只能处理这种形式
它是一种思想,一种美妙的剪枝
应用:
例题一:
https://www.luogu.org/problem/P3935
给定两个数L,R(L<=R<=1e9),求[L,R]中每个元素的约数个数和
分析:
首先很明显一个前缀和,剩下的问题成了求[1,X]中每个元素的约数个数和
暴力不行,就只有分别考虑每个约数出现的次数了
考虑[1,X]中以i为倍数的有X/i个
所以答案就成了Σn/i
code by wzxbeliever:
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ri register int
#define lowbit(x) x&(-x)
using namespace std;
const int mod=998244353;
ll l,r;
il ll solve(ll x){
ll ans=0;
for(register ll i=1,j;i<=x;i=j+1){
j=x/(x/i);
ans+=(j-i+1)*(x/i)%mod;
}
return ans;
}
int main(){
scanf("%lld%lld",&l,&r);
printf("%lld\n",(solve(r)-solve(l-1)+mod)%mod);
return 0;
}
插入一个复习块(和本题无关):
例题二:
https://www.luogu.org/problem/P2261
分析:
code:
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
int main() {
ll n,k;
scanf("%lld%lld",&n,&k);
ll ans=n*k;
for(ll l=1,r;l<=n;l=r+1) {
if(k/l) r=min(k/(k/l),n);
else r=n;
ans-=(k/l)*(r-l+1)*(l+r)/2;
}
printf("%lld",ans);
return 0;
}
由上题分析,不仅可以维护Σn/i,还可以维护iΣn/i,还可以维护iiΣn/i,还可以维护前缀和已知的数组a[i]组成的Σa[i]*n/i
例题三:
分析:
分别算出(Nmodi)和(Mmodj)
再将两者相乘
即为答案
例题四:
吐槽:
心路历程:1.考虑每段的贡献,但是要处理每个K,不现实
2.考虑先不看节点,再找到一些规律,做一些变换得答案,但是貌似没啥规律结论
分析:
同样发现有很多的K的值不同,但得出的答案是一样的
考虑整除分块
将询问离线成三个点的询问,每个询问都是形如求f(u,1,w)的值。
考虑每条边的贡献,整除分块后每次相当于将子树内每个点的f(u,1,l)~f(u,1,r)都加上一个值。
于是我们最后再 dfs 一遍,经过一条边的时候,将这条边的边权整除分块,并在树状数组的对应区间上 修改,退出的时候再将该边的影响去除。遍历到某个点查询的时候,直接查询树状数组某个点的值即 可
code by std:
#include <bits/stdc++.h>
template <class T>
inline void read(T &x)
{
static char ch;
while (!isdigit(ch = getchar()));
x = ch - '0';
while (isdigit(ch = getchar()))
x = x * 10 + ch - '0';
}
template <class T>
inline void putint(T x)
{
static char buf[25], *tail = buf;
if (!x)
putchar('0');
else
{
for (; x; x /= 10) *++tail = x % 10 + '0';
for (; tail != buf; --tail) putchar(*tail);
}
}
typedef long long s64;
const int MaxNV = 1e5 + 5;
const int MaxNE = MaxNV << 1;
const int MaxLog = 18;
struct request
{
int d, opt, pos;
request(){}
request(int y, int z, int p):
d(y), opt(z), pos(p) {}
};
int n, m = 30000, Q;
s64 bit[MaxNV], ans[MaxNV];
std::vector<int> add[MaxNV];
std::vector<request> req[MaxNV];
int ect, adj[MaxNV];
int to[MaxNE], e_w[MaxNE], nxt[MaxNE];
int dep[MaxNV];
int anc[MaxNV][MaxLog + 1];
#define trav(u) for (int e = adj[u], v, w; v = to[e], w = e_w[e], e; e = nxt[e])
inline void addEdge(int u, int v, int w)
{
nxt[++ect] = adj[u];
adj[u] = ect;
e_w[ect] = w;
to[ect] = v;
}
inline void bit_modify(int x, int del)
{
for (; x <= m; x += x & -x)
bit[x] += del;
}
inline s64 bit_query(int x)
{
s64 res = 0;
for (; x; x ^= x & -x)
res += bit[x];
return res;
}
inline int query_lca(int u, int v)
{
if (dep[u] < dep[v])
std::swap(u, v);
for (int i = 0, d = dep[u] - dep[v]; d; d >>= 1, ++i)
if (d & 1)
u = anc[u][i];
if (u == v)
return u;
for (int i = MaxLog; i >= 0; --i)
if (anc[u][i] != anc[v][i])
{
u = anc[u][i];
v = anc[v][i];
}
return anc[u][0];
}
inline void dfs_init(int u)
{
for (int i = 0; anc[u][i]; ++i)
anc[u][i + 1] = anc[anc[u][i]][i];
trav(u) if (v != anc[u][0])
{
anc[v][0] = u;
dep[v] = dep[u] + 1;
dfs_init(v);
}
}
inline void modify(int x, int opt)
{
int lst = 0, lst_del = 0; --x;
for (int cur = 1; cur <= x; cur = lst + 1)
{
lst = x / (x / cur);
bit_modify(cur, +opt * (x / cur + 1) - lst_del);
lst_del = opt * (x / cur + 1);
}
bit_modify(x + 1, opt - lst_del);
}
inline void dfs_answer(int u)
{
int cnt_req = req[u].size();
for (int j = 0; j < cnt_req; ++j)
{
int d = req[u][j].d, opt = req[u][j].opt;
ans[req[u][j].pos] += bit_query(d) * opt;
}
trav(u) if (v != anc[u][0])
{
modify(w, +1);
dfs_answer(v);
modify(w, -1);
}
}
int main()
{
freopen("delivery.in", "r", stdin);
freopen("delivery.out", "w", stdout);
read(n), read(Q);
for (int i = 1; i < n; ++i)
{
int u, v, w;
read(u), read(v), read(w);
addEdge(u, v, w), addEdge(v, u, w);
}
dfs_init(1);
for (int i = 1; i <= Q; ++i)
{
int u, v, w, z;
read(u), read(v), read(w);
z = query_lca(u, v);
ans[i] = 1;
req[u].push_back(request(w, +1, i));
req[v].push_back(request(w, +1, i));
req[z].push_back(request(w, -2, i));
}
dfs_answer(1);
for (int i = 1; i <= Q; ++i)
putint(ans[i]), putchar('\n');
return 0;
}
其实也可以平衡规划
这一个阙值K,小于k的暴力预处理,大于k的主席树
code by hs:
#include<bits/stdc++.h>
using namespace std;
#define maxn 100010
#define maxm 210
template<typename T>T read()
{
T res=0;
int sym=1;
char cc=getchar();
while(!isdigit(cc)&&cc!='-')
cc=getchar();
if(cc=='-')sym=-1,cc=getchar();
while(!isdigit(cc))cc=getchar();
while(isdigit(cc))res=res*10+cc-'0',cc=getchar();
return sym*res;
}
template<typename T>void read(T &o)
{
o=read<T>();
}
template<typename A,typename... B>void read(A& o,B&... Others)
{
o=read<A>();
read(Others...);
}
struct Edge
{
int v;
int w;
Edge *next;
Edge(int a=0,int b=0,Edge *c=NULL)
{
v=a;
w=b;
next=c;
}
}*head[maxn];
struct Node
{
int v;
int l;
int r;
}node[maxn*50];
int n,m,mx,cnt,T[maxn],dep[maxn],vis[maxm],fa[maxn][19];
long long tot[maxm][maxn];
void insert(int &a,int b,int l,int r,int x)
{
a=++cnt;
node[a]=node[b];
node[a].v+=1;
if(l==r)return ;
int mid=(l+r)>>1;
if(x<=mid)insert(node[a].l,node[b].l,l,mid,x);
else insert(node[a].r,node[b].r,mid+1,r,x);
}
int query(int a,int b,int c,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
return node[a].v+node[b].v-2*node[c].v;
int mid=(l+r)>>1,ans=0;
if(x<=mid)
ans+=query(node[a].l,node[b].l,node[c].l,l,mid,x,y);
if(y>mid)
ans+=query(node[a].r,node[b].r,node[c].r,mid+1,r,x,y);
return ans;
}
void dfs(int k)
{
dep[k]=dep[fa[k][0]]+1;
for(int i=1;i<=18;i++)
fa[k][i]=fa[fa[k][i-1]][i-1];
for(Edge *i=head[k];i!=NULL;i=i->next)
{
if(i->v==fa[k][0])
continue;
fa[i->v][0]=k;
insert(T[i->v],T[k],1,mx,i->w);
dfs(i->v);
}
}
void dfs2(int k,int t)
{
for(Edge *i=head[k];i!=NULL;i=i->next)
{
if(i->v==fa[k][0])
continue;
tot[t][i->v]=tot[t][k]+(i->w-1)/t;
dfs2(i->v,t);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
int d=dep[x]-dep[y];
for(int i=0;i<=18;i++)
if(d&(1<<i))
x=fa[x][i];
if(x==y)return x;
for(int i=18;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main()
{
freopen("delivery.in","r",stdin);
freopen("delivery.out","w",stdout);
read(n,m);
for(int i=1;i<n;i++)
{
int x,y,z;
read(x,y,z);
mx=max(mx,z);
head[x]=new Edge(y,z,head[x]);
head[y]=new Edge(x,z,head[y]);
}
dfs(1);
for(int i=1;i<=m;i++)
{
int x,y,z;
read(x,y,z);
if(x==y)
{
puts("1");
continue;
}
int l=lca(x,y);
if(z<=200)
{
if(!vis[z])
{
dfs2(1,z);
vis[z]=true;
}
printf("%lld\n",dep[x]+dep[y]-dep[l]-dep[fa[l][0]]+
tot[z][x]+tot[z][y]-tot[z][l]*2);
continue;
}
int ans=0;
for(int j=2;(j-1)*z+1<=mx;j++)
{
ans+=(j-1)*
query(T[x],T[y],T[l],1,mx,(j-1)*z+1,min(mx,j*z));
}
printf("%d\n",ans+dep[x]+dep[y]-dep[l]-dep[fa[l][0]]);
}
return 0;
}