树分块
板子
//
// Created by Artist on 2021/11/9.
//
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define endl '\n'
void io() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); }
const int maxn = 1e3+4;
stack<int> st;
vector<int> G[maxn];
int k,n,b;
int bel[maxn],proid[maxn];
void dfs(int u,int fa) {
int csz = st.size();
for(auto v:G[u]) {
if(v==fa) continue;
dfs(v,u);
int nsz = st.size();
if(nsz-csz>=b) {
proid[++k] = u;
while(st.size()>csz) {
int v=st.top();
bel[v] = k;
st.pop();
}
}
}
st.push(u);
}
signed main() {
io();
cin>>n>>b;
for(int i=1,u,v;i<n;++i) {
cin>>u>>v;
G[u].pb(v);
G[v].pb(u);
}
dfs(1,-1);
if(!k) ++k;
proid[k] = 1;
while(st.size()) {
int v=st.top();
bel[v] = k;
st.pop();
}
cout<<k<<endl;
for(int i=1;i<=n;++i) cout<<bel[i]<<" ";
cout<<endl;
for(int i=1;i<=k;++i) cout<<proid[i]<<" ";
cout<<endl;
}
#58. 【WC2013】糖果公园
树上(括号序列)带修莫队
//
// Created by Artist on 2021/11/9.
//
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define endl '\n'
void io() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); }
#define typeinput int
int n,m,q;
const int maxn = 1e6+6;
int v[maxn],w[maxn],c[maxn],vis[maxn],pos[maxn];
ll ans;
vector<int> G[maxn];
int cntq,cntu;
struct query{
int x,y,t,id;
bool operator < (const query &b) const {
return (pos[x]<pos[b.x])||(pos[x]==pos[b.x]&&pos[y]<pos[b.y])||
(pos[x]==pos[b.x]&&pos[y]==pos[b.y]&&t<b.t);
}
}qy[maxn];
struct update{
int x,y,ori;
}ud[maxn];
int ord[maxn],dfn,in[maxn],out[maxn],f[maxn][21],dep[maxn];
int lst[maxn],cnt[maxn]; // 当前每种糖果的数目
void dfs(int x,int fa) {
f[x][0]=fa;
ord[in[x]=++dfn]=x;
for(auto y:G[x]) {
if(y-fa) dep[y]=dep[x]+1,dfs(y,x);
}
ord[out[x]=++dfn]=x;
}
int lca(int x,int y) {
if(dep[x]<dep[y]) swap(x,y);
for(int j=20;~j;--j) if(f[x][j]&&dep[f[x][j]]>=dep[y])
x=f[x][j];
if(x==y) return x;
for(int j=20;~j;--j) if(f[x][j]&&f[x][j]!=f[y][j])
x=f[x][j],y=f[y][j];
return f[x][0];
}
void add(int x) {
// 删掉
if (vis[x]) ans -= 1ll * w[cnt[c[x]]--] * v[c[x]];
else ans += 1ll * w[++cnt[c[x]]] * v[c[x]];
vis[x] ^= 1;
}
ll res[maxn];
signed main() {
io();
cin>>n>>m>>q;
for(int i=1;i<=m;++i) cin>>v[i];
for(int i=1;i<=n;++i) cin>>w[i];
for(int i=1,x,y;i<n;++i) cin>>x>>y,G[x].pb(y),G[y].pb(x);
for(int i=1;i<=n;++i) cin>>c[i],lst[i]=c[i];
dfs(1,0);
for(int j=1;j<=20;++j) for(int i=1;i<=n;++i) f[i][j]=f[f[i][j-1]][j-1];
int blk = pow(dfn,2.0/3);
for(int i=1;i<=dfn;++i) pos[i]=(i-1)/blk; // 每个位置属于哪个块
for(int i=1;i<=q;++i) {
int t,x,y;cin>>t>>x>>y;
if(!t) {
++cntu;
ud[cntu].x = x;
ud[cntu].ori = lst[x];
lst[x] = ud[cntu].y = y;
} else {
++cntq;
if(in[x]>in[y]) swap(x,y);
if(lca(x,y)==x) {
qy[cntq] = (query) {in[x],in[y],cntu,cntq};
} else {
qy[cntq] = (query) {out[x],in[y],cntu,cntq};
}
}
}
sort(qy+1,qy+1+cntq);
for(int i=1,l=1,r=0,t=0;i<=cntq;++i) {
while(l>qy[i].x) {
--l;
add(ord[l]);
}
while(r<qy[i].y) {
++r;
add(ord[r]);
}
while(l<qy[i].x) {
add(ord[l]);
l++;
}
while(r>qy[i].y) {
add(ord[r]);
r--;
}
while(t<qy[i].t) {
t++;
if(vis[ud[t].x]) {
add(ud[t].x); // 把原来的删掉
c[ud[t].x] = ud[t].y;
add(ud[t].x); // 把新的加一下
} else c[ud[t].x] = ud[t].y;
}
while(t>qy[i].t) {
if(vis[ud[t].x]) {
add(ud[t].x);
c[ud[t].x] = ud[t].ori;
add(ud[t].x);
} else c[ud[t].x] = ud[t].ori;
t--;
}
// 当前区间
int xx = ord[l], yy = ord[r];
int LCA = lca(xx,yy);
if(xx!=LCA && yy!=LCA) {
add(LCA);
res[qy[i].id] = ans;
add(LCA);
} else res[qy[i].id] = ans;
}
for(int i=1;i<=cntq;++i) cout<<res[i]<<endl;
}
二逼平衡树
- 分块
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;const int N=5*1e4+10;const int M=1e5+10;
const int B=260;const int B2=300;int a[N];//维护的表格
int cnt1[N/B+3][M/B2+3];int cnt2[N/B+3][M];int n;int m;int bi[N];int bi1[M];
int tr1[M];int tr2[M];map <int,int> mp;int S;int Pr[M];int Pl[M];int val[M];
inline int frk(int l,int r,int va)//查询元素的排名
{
int p1=bi[l];int p2=bi[r];int ret=0;
if(p1==p2){for(int i=l;i<=r;i++)ret+=(a[i]<va);return ret+1;}
for(int i=l;bi[i]==p1;i++)ret+=(a[i]<va);
for(int i=r;bi[i]==p2;i--)ret+=(a[i]<va);p2--;
for(int i=1;i<bi1[va];i++)ret+=cnt1[p2][i];
for(int i=1;i<bi1[va];i++)ret-=cnt1[p1][i];
for(int i=va-1;bi1[i]==bi1[va];i--)ret+=cnt2[p2][i];
for(int i=va-1;bi1[i]==bi1[va];i--)ret-=cnt2[p1][i];return ret+1;
}
inline int ckth(const int& p1,const int& p2,int k)//辅助函数,查询kth
{
int ret=B2;int cur=0;
for(int t=1;cur<k;ret+=B2,t++)cur+=cnt1[p2][t]-cnt1[p1][t]+tr1[t];ret-=B2;
for(;cur>=k;ret--)cur-=cnt2[p2][ret]-cnt2[p1][ret]+tr2[ret];return ret+1;
}
inline int cpre(const int& p1,const int& p2,int k)//辅助函数,查询前驱
{
for(int i=k-1;bi1[i]==bi1[k];i--)
if(cnt2[p2][i]-cnt2[p1][i]+tr2[i])return i;
int p;for(p=bi1[k]-1;(cnt1[p2][p]-cnt1[p1][p]+tr1[p])==0;p--);
for(int i=Pr[p];;i--)if(cnt2[p2][i]-cnt2[p1][i]+tr2[i])return i;
}
inline int csuf(const int& p1,const int& p2,int k)//辅助函数,查询后继
{
for(int i=k+1;bi1[i]==bi1[k];i++)
if(cnt2[p2][i]-cnt2[p1][i]+tr2[i])return i;
int p;for(p=bi1[k]+1;(cnt1[p2][p]-cnt1[p1][p]+tr1[p])==0;p++);
for(int i=Pl[p];;i++)if(cnt2[p2][i]-cnt2[p1][i]+tr2[i])return i;
}
# define ins(x) tr1[bi1[x]]++,tr2[x]++
# define del(x) tr1[bi1[x]]--,tr2[x]--
inline int calc(int l,int r,int k,int(*f)(const int& p1,const int& p2,int k))//这里用了个函数指针
{
int p1=bi[l];int p2=bi[r];int ret=0;//直接处理出区间的cnt1,cnt2数组
if(p1==p2)
{
for(int i=l;i<=r;i++)ins(a[i]);ret=f(p1,p2,k);
for(int i=l;i<=r;i++)del(a[i]);return val[ret];
}
for(int i=l;bi[i]==p1;i++)ins(a[i]);
for(int i=r;bi[i]==p2;i--)ins(a[i]);ret=f(p1,p2-1,k);
for(int i=l;bi[i]==p1;i++)del(a[i]);
for(int i=r;bi[i]==p2;i--)del(a[i]);return val[ret];//记得还原回离散化之前的值
}
inline void modify(int pos,int y)//暴力修改
{
int p=bi1[a[pos]];for(int i=bi[pos];i<=bi[n];i++)cnt1[i][p]--;
p=a[pos];for(int i=bi[pos];i<=bi[n];i++)cnt2[i][p]--;
p=bi1[y];for(int i=bi[pos];i<=bi[n];i++)cnt1[i][p]++;
for(int i=bi[pos];i<=bi[n];i++)cnt2[i][y]++;a[pos]=y;
}
struct opt{int tp;int l;int r;int k;}op[N];
int main()
{
scanf("%d%d",&n,&m);mp[-2147483647]=1;mp[2147483647]=1;
for(int i=1;i<=n;i++)scanf("%d",&a[i]),mp[a[i]]=1;
for(int i=1;i<=m;i++)
{
scanf("%d",&op[i].tp);
if(op[i].tp!=3)scanf("%d%d%d",&op[i].l,&op[i].r,&op[i].k);
else scanf("%d%d",&op[i].l,&op[i].k);if(op[i].tp!=2)mp[op[i].k]=1;
}
S=mp.size();map <int,int> :: iterator it,it1;//离散化
for(it=mp.begin(),it1=it,++it1;it1!=mp.end();++it,++it1)it1->second+=it->second;
for(it=mp.begin();it!=mp.end();++it)val[it->second]=it->first;
for(int i=1;i<=n;i++)a[i]=mp[a[i]];
for(int i=1;i<=m;i++)if(op[i].tp!=2)op[i].k=mp[op[i].k];
for(int i=1;i<=n;i++)bi[i]=(i-1)/B+1;for(int i=1;i<=S;i++)bi1[i]=(i-1)/B2+1;
for(int i=1;i<=S;i++)Pr[bi1[i]]=i;for(int i=S;i>=1;i--)Pl[bi1[i]]=i;
// 预处理cnt1和cnt2数组
for(int i=1;i<=n;i++)
{
int p=bi1[a[i]];for(int j=bi[i];j<=bi[n];j++)cnt1[j][p]++;
p=a[i];for(int j=bi[i];j<=bi[n];j++)cnt2[j][p]++;
}ins(1);ins(S);//插入哨兵
for(int i=1;i<=m;i++)
switch(op[i].tp)
{
case 1:{printf("%d\n",frk(op[i].l,op[i].r,op[i].k));break;}
case 2:{printf("%d\n",calc(op[i].l,op[i].r,op[i].k+1,ckth));break;}
case 3:{modify(op[i].l,op[i].k);break;}
case 4:{printf("%d\n",calc(op[i].l,op[i].r,op[i].k,cpre));break;}
case 5:{printf("%d\n",calc(op[i].l,op[i].r,op[i].k,csuf));break;}
}return 0;//拜拜程序~
}
- 线段树套平衡树
#include <algorithm>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <complex>
#include <string>
#include <cstdio>
#include <vector>
#include <bitset>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
const int MAXN = 1e5 + 100;
int n, m;
int a[MAXN];
namespace Treap {
struct balanced {
int w;
int sz;
int num;
int fix;
int ch[2];
};
int tot;
balanced tree[MAXN * 20];
int newnode(int w) {
++tot;
tree[tot].w = w;
tree[tot].fix = rand();
tree[tot].num = 1;
tree[tot].ch[0] = tree[tot].ch[1] = 0;
tree[tot].sz = 1;
return tot;
}
void pushup(int p) {
tree[p].sz = tree[tree[p].ch[0]].sz + tree[tree[p].ch[1]].sz + tree[p].num;
}
void rotate(int &p, int d) {
int y = tree[p].ch[d];
tree[p].ch[d] = tree[y].ch[d ^ 1];
tree[y].ch[d ^ 1] = p;
pushup(p);
pushup(y);
p = y;
}
void insert(int &p, int w) {
if (!p)
p = newnode(w);
else if (tree[p].w == w)
++tree[p].num;
else {
if (tree[p].w > w) {
insert(tree[p].ch[0], w);
if (tree[tree[p].ch[0]].fix > tree[p].fix)
rotate(p, 0);
} else {
insert(tree[p].ch[1], w);
if (tree[tree[p].ch[1]].fix > tree[p].fix)
rotate(p, 1);
}
}
pushup(p);
}
void remove(int &p, int w) {
if (tree[p].w > w)
remove(tree[p].ch[0], w);
else if (tree[p].w < w)
remove(tree[p].ch[1], w);
else {
if (tree[p].num > 1)
--tree[p].num;
else {
if (!tree[p].ch[0] && !tree[p].ch[1])
p = 0;
else if (!tree[p].ch[0]) {
rotate(p, 1);
remove(tree[p].ch[0], w);
} else if (!tree[p].ch[1]) {
rotate(p, 0);
remove(tree[p].ch[1], w);
} else {
if (tree[tree[p].ch[0]].fix > tree[tree[p].ch[1]].fix) {
rotate(p, 0);
remove(tree[p].ch[1], w);
} else {
rotate(p, 1);
remove(tree[p].ch[0], w);
}
}
}
}
if (p)
pushup(p);
}
int queryrank(int p, int k) // return the highest rank of value 'k'
{
if (!p)
return 0;
if (tree[p].w > k)
return queryrank(tree[p].ch[0], k);
else if (tree[p].w == k)
return tree[tree[p].ch[0]].sz;
else
return tree[tree[p].ch[0]].sz + tree[p].num + queryrank(tree[p].ch[1], k);
}
int querynum(int p, int k) // return the value of kth rank node
{
if (tree[tree[p].ch[0]].sz + 1 == k)
return tree[p].w;
else if (tree[tree[p].ch[0]].sz + 1 < k)
return querynum(tree[p].ch[1], k - 1 - tree[tree[p].ch[0]].sz);
else
return querynum(tree[p].ch[0], k);
}
int querypre(int p, int k) // return the prefix of value k
{
if (!p)
return -2147483647;
if (tree[p].w >= k)
return querypre(tree[p].ch[0], k);
else
return max(tree[p].w, querypre(tree[p].ch[1], k));
}
int querysuf(int p, int k) // return the suffix of value k
{
if (!p)
return 2147483647;
if (tree[p].w <= k)
return querysuf(tree[p].ch[1], k);
else
return min(tree[p].w, querysuf(tree[p].ch[0], k));
}
void listall(int p) {
if (tree[p].ch[0])
listall(tree[p].ch[0]);
cerr << tree[p].w << ",sz=" << tree[p].num << " ";
if (tree[p].ch[1])
listall(tree[p].ch[1]);
}
}
using Treap::listall;
namespace SEG {
struct segment {
int l;
int r;
int root;
};
segment tree[MAXN * 8];
void build(int p, int l, int r) {
tree[p].l = l;
tree[p].r = r;
for (int i = l; i < r + 1; ++i)
Treap::insert(tree[p].root, a[i]);
if (l != r) {
int mid = (l + r) / 2;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
}
}
void modify(int p, int x, int y) {
Treap::remove(tree[p].root, a[x]);
Treap::insert(tree[p].root, y);
if (tree[p].l == tree[p].r)
return;
int mid = (tree[p].l + tree[p].r) / 2;
if (x > mid)
modify(p * 2 + 1, x, y);
else
modify(p * 2, x, y);
}
int queryrank(int p, int l, int r, int k) // query the highest rank of value 'k'
{
if (tree[p].l > r || tree[p].r < l)
return 0;
if (tree[p].l >= l && tree[p].r <= r)
return Treap::queryrank(tree[p].root, k);
else
return queryrank(p * 2, l, r, k) + queryrank(p * 2 + 1, l, r, k);
}
int querynum(int u, int v, int k) // query the value of kth num
{
int l = 0, r = 1e8;
while (l < r) {
int mid = (l + r + 1) / 2;
if (queryrank(1, u, v, mid) < k)
l = mid;
else
r = mid - 1;
}
return r;
}
int querypre(int p, int l, int r, int k) {
if (tree[p].l > r || tree[p].r < l)
return -2147483647;
if (tree[p].l >= l && tree[p].r <= r)
return Treap::querypre(tree[p].root, k);
else
return max(querypre(p * 2, l, r, k), querypre(p * 2 + 1, l, r, k));
}
int querysuf(int p, int l, int r, int k) {
if (tree[p].l > r || tree[p].r < l)
return 2147483647;
if (tree[p].l >= l && tree[p].r <= r)
return Treap::querysuf(tree[p].root, k);
else
return min(querysuf(p * 2, l, r, k), querysuf(p * 2 + 1, l, r, k));
}
}
int read() {
char ch = getchar();
int x = 0, flag = 1;
while (ch != '-' && (ch < '0' || ch > '9'))
ch = getchar();
if (ch == '-') {
ch = getchar();
flag = -1;
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * flag;
}
// CDQ分治(整体二分)和树套树好像可以交换
int main() {
n = read();
m = read();
for (int i = 1; i < n + 1; ++i)
a[i] = read();
SEG::build(1, 1, n);
for (int i = 0; i < m; ++i) {
int opt = read();
if (opt == 3) {
int x = read(), y = read();
SEG::modify(1, x, y);
a[x] = y;
} else {
int l = read(), r = read(), k = read();
if (opt == 1)
printf("%d\n", SEG::queryrank(1, l, r, k) + 1);
else if (opt == 2)
printf("%d\n", SEG::querynum(l, r, k));
else if (opt == 4)
printf("%d\n", SEG::querypre(1, l, r, k));
else
printf("%d\n", SEG::querysuf(1, l, r, k));
}
}
return 0;
}
树套树
Yu Ling(Ling YueZheng) and Colorful Tree
树状数组套线段树
#include<cstdio>
#include<vector>
// 树状数组套动态开点线段树
#define lowbit(x) x&(-x)
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int maxn=110010;
int n,m,cnt;
int f[maxn][20],st[maxn],ed[maxn];
ll dis[maxn],ans[maxn];
struct node{
int x,l,r,id;
};
vector<P> g[maxn];
vector<node> q[maxn];
vector<int> d[maxn];
void dfs(int x,int fa){ // 求出dfn序以及祖先
st[x]=++cnt;f[x][0]=fa;
for(int i=1;i<=19;++i)
f[x][i]=f[f[x][i-1]][i-1];
for(auto i:g[x]){
int v=i.first;
if(v==fa)continue;
dis[v]=dis[x]+i.second; // 离1的距离
dfs(v,x);
}
ed[x]=cnt;
}
const ll inf=1e18;
int rt[maxn],ls[maxn<<7],rs[maxn<<7],sum[maxn<<7];
// 动态开点线段树:普通的单点修改和区间查询
void add(int &rt,int x,int k,int l,int r){
if(!rt)rt=++cnt;
sum[rt]+=k;
if(l==r)return;
int mid=(l+r)>>1;
if(x<=mid)add(ls[rt],x,k,l,mid);
else add(rs[rt],x,k,mid+1,r);
}
int query(int rt,int le,int ri,int l,int r){
if(!rt||(l>=le&&r<=ri))return sum[rt];
int mid=(l+r)>>1;
int res=0;
if(le<=mid)res+=query(ls[rt],le,ri,l,mid);
if(ri>mid)res+=query(rs[rt],le,ri,mid+1,r);
return res;
}
void clear(int x){
while(x<=n){
rt[x]=0;
x+=lowbit(x);
}
}
void upd(int x,int y,int k){
while(x<=n){
add(rt[x],y,k,1,n);
x+=lowbit(x);
}
}
int ask(int x,int l,int r){
int res=0;
while(x){
res+=query(rt[x],l,r,1,n);
x-=lowbit(x);
}
return res;
}
// 对每个因子,处理答案
// 本质有点像cdq...
void solve(int x){
int num=0;cnt=0;
for(auto i:q[x])
if(i.id)++num; // 询问个数
for(auto i:q[x]){// i是按照时间顺序(q内部是按照时间顺序)
if(!num)break;
if(!i.id){ // 倍数,加点
// 树状数组差分:区间修改
upd(st[i.x],i.l,1);
upd(ed[i.x]+1,i.l,-1);
}else{
--num;
// 查询
int res=ask(st[i.x],i.l,i.r);
if(!res){
ans[i.id]=-1;
continue;
}
// 存在的话,倍增到那个点,求个距离
int p=i.x;
for(int j=19;j>=0;--j)
if(f[p][j]&&ask(st[f[p][j]],i.l,i.r)==res)
p=f[p][j];
ans[i.id]=dis[i.x]-dis[p];
}
}
while(cnt)ls[cnt]=rs[cnt]=sum[cnt]=0,--cnt; // 删除线段树
// 删除树状数组
for(auto i:q[x])
if(!i.id){
clear(st[i.x]);
clear(ed[i.x]+1);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;++i){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
g[x].push_back({y,z});
g[y].push_back({x,z});
}
for(int i=1;i<=n;++i)
for(int j=i;j<=n;j+=i)
d[j].push_back(i); // i的所有因子
dfs(1,0);
for(int i=1;i<=m;++i){
int op,u,l,r,x;
scanf("%d%d%d",&op,&u,&l);
if(!op){
// 将节点u涂上l,会对所有因子产生一定贡献
for(int v:d[l])
q[v].push_back((node){u,l,0,0}); // 对于每个因子,push进这一个节点
ans[i]=-inf;
}else{
scanf("%d%d",&r,&x);
if((l-1)/x==r/x){
ans[i]=-1;
continue;
}
// 加入询问
q[x].push_back((node){u,l,r,i});
}
}
for(int i=1;i<=n;++i)
solve(i);
for(int i=1;i<=m;++i)
if(ans[i]!=-inf){
if(ans[i]==-1)printf("Impossible!\n");
else printf("%lld\n",ans[i]);
}
return 0;
}
P3768 简单的数学题
莫反+督教筛,常规推导。
//
// Created by Artist on 2021/11/10.
//
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define endl '\n'
void io() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); }
#define typeinput int
#define int long long
int p,n;
map<ll,int> mp;
int qpow(int a,int nn) {
int ans=1;
while(nn) {
if(nn&1) ans=ans*a%p;
a=a*a%p;
nn>>=1;
}
return ans;
}
int d2;
int d6;
const int maxn = 5e6+4;
int pri[maxn],cnt,vis[maxn],phi[maxn],pphi[maxn];
inline void init() {
phi[1]=1;
for(int i=2;i<maxn;++i) {
if(!vis[i]) pri[++cnt]=i,phi[i]=i-1;
for(int j=1;j<=cnt&&1ll*pri[j]*i<maxn;++j) {
vis[i*pri[j]] = 1;
if(i%pri[j]) {
phi[i*pri[j]] = 1ll*phi[i]*(pri[j]-1)%p;
} else {
phi[i*pri[j]] = 1ll*phi[i]*pri[j]%p;
break;
}
}
}
for(int i=1;i<maxn;++i) pphi[i]=(1ll*phi[i]*i%p*i%p+pphi[i-1])%p;
}
ll sum(ll x) {x%=p;return x*(x+1)%p*d2%p;}
ll sum2(ll x) {x%=p;return x*(x+1)%p*(x+x+1)%p*d6%p;}
inline int prephi(ll x) {
if(x<maxn) return pphi[x];
if(mp[x]) return mp[x];
int ans = sum(x);
ans=ans*ans%p;
for(int i=2,j;i<=x;i=j+1) {
j=x/(x/i);
int tmp = (sum2(j)-sum2(i-1)+p)%p;
ans = (ans-tmp*prephi(x/i)%p+p)%p;
}
return mp[x] = ans;
}
inline int solve() {
int ans=0;
int lst = 0;
for(int i=1,j;i<=n;i=j+1) {
j=n/(n/i);
int phip = prephi(j);
int tp=sum(n/i);
ans = (ans + 1ll*tp*tp%p*((phip-lst+p)%p))%p;
lst = phip;
}
return ans;
}
signed main() {
io();
cin>>p>>n;
d2 = qpow(2,p-2);
d6 = qpow(6,p-2);
init();
cout<<solve()<<endl;
}
ICPC台北 K. Number with Bachelors
数位dp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
char s[105], op[2], ans[105];
bool dflag;
int type;
ull dp[2][20][1<<16];
int convert(char c)
{
if(isdigit(c)) return c - '0';
return c - 'a' + 10;
}
char convert2(int x)
{
if(x<=9) return char('0' + x);
return char('a' + x - 10);
}
ull conv10(char *s)
{
ull x = 0;
int n = strlen(s);
for(int i=0; i<n; i++) x = x*10 + convert(s[i]);
return x;
}
ull conv16(char *s)
{
ull x = 0;
int n = strlen(s);
for(int i=0; i<n; i++) x = x*16 + convert(s[i]);
return x;
}
void print16(ull x)
{
int tp = 0;
static char stk[105];
if(!x) puts("0");
else
{
while(x)
{
stk[++tp] = convert2(x%16);
x /= 16;
}
for(int i=tp; i>=1; i--) putchar(stk[i]);
puts("");
}
}
// z=1:当前前面有先导零,需要加别的数字
ull DP(int b, int lim, int z, int st)
{
if(b<0) return 1;
auto &x = dp[dflag][b][st];
if(!lim&&!z&&~x) return x;
int up = lim ? convert(s[b]) : (dflag ? 9 : 15);
ull ans = 0;
for(int i=0; i<=up; i++)
{
if(z&&!i) ans += DP(b-1, lim&&i==up, z, st);
else
{
if((st>>i)&1) continue;
ans += DP(b-1, lim&&i==up, 0, st|(1<<i));
}
}
if(!lim&&!z) x = ans; // 记忆化:没有先导零且不贴上限时位数为b的答案
return ans;
}
ull gao(char *s)
{
int n = strlen(s);
reverse(s, s+n);
if(dflag && n>10) return DP(9, 0, 1, 0);
if(!dflag && n>16) return DP(15, 0, 1, 0);
return DP(n-1, 1, 1, 0);
}
int check(char *s)
{
int st = 0, n = strlen(s);
for(int i=0; i<n; i++)
{
int d = convert(s[i]);
if((st>>d)&1) return 0; // 如果这个边界不合法,返回0
st |= (1<<d);
}
return 1; // 如果这个边界合法,返回1
}
void Try(int b, int z, int st, ull rk)
{
if(b<0)
{
int st = (dflag ? 9 : 15);
while(st && !ans[st]) --st;
for(int i=st; i>=0; i--) putchar(convert2(ans[i]));
puts("");
return;
}
int up = dflag ? 9 : 15;
for(int i=0; i<=up; i++)
{
ans[b] = i;
if(z&&!i)
{
ull cnt = DP(b-1, 0, z, st);
if(cnt<rk) rk -= cnt;
else
{
Try(b-1, z, st, rk);
return;
}
}
else
{
if((st>>i)&1) continue;
ull cnt = DP(b-1, 0, 0, st|(1<<i));
if(cnt<rk) rk -= cnt;
else
{
Try(b-1, 0, st|(1<<i), rk);
return;
}
}
}
puts("-");
}
void solve()
{
scanf("%s%d", op, &type);
if(op[0]=='d') dflag = 1;
else dflag = 0;
if(!type)
{
scanf("%s", s);
ull L = gao(s) - check(s); // check函数的使用帮助我们不用进行L=L-1的转移,因为这个是字串,会比较麻烦
scanf("%s", s);
ull R = gao(s);
if(dflag) printf("%llu\n", R - L);
else print16(R-L);
}
else
{
scanf("%s", s);
if(dflag) Try(9, 1, 0, conv10(s));
else Try(15, 1, 0, conv16(s));
}
}
int main()
{
memset(dp, -1, sizeof(dp));
int _; scanf("%d", &_);
while(_--) solve();
return 0;
}