十一月刷题记录3

树分块

板子

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

二逼平衡树

  1. 分块
#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;//拜拜程序~
}


  1. 线段树套平衡树
#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值