zoukankan      html  css  js  c++  java
  • [题解] 杂题记录 (2021/10/29)

    XR-4 混乱度

    题目

    链接

    简答

    根据 Kummer 定理,(inom{a_1+a_2+cdots+a_n}{a_1,a_2,cdots,a_n}) 在模 (p) 意义下不为0当前仅当 (p) 进制下不进位

    考虑固定右端点枚举左端点,(p) 进制下所有位上的和都必须小于 (p) ,所有至多往左枚举 (plog_p A) 项,但这样是 (log_p^2A) 的,直接枚举非 (0) 位就是 (mathcal{O}left(nplog_p A ight))

    代码

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    constexpr int N=500010;
    int p,dg[N],num[N][100][2],n,cnt[100],lim,inv[100],fac[100],ifac[100];ull a[N];
    int main(){
        scanf("%d%d",&n,&p);if(p==2) lim=60;else if(p==3) lim=40;else if(p==5) lim=30;else lim=25;
        inv[1]=1;for(int i=2;i<p;++i) inv[i]=(p-p/i)*inv[p%i]%p;
        fac[0]=ifac[0]=1;for(int i=1;i<p;++i) fac[i]=fac[i-1]*i%p,ifac[i]=inv[fac[i]];
        for(int i=1;i<=n;++i){
            scanf("%llu",&a[i]);
            for(int j=0;j<lim;++j){
                if(a[i]%p) num[i][dg[i]][0]=j,num[i][dg[i]][1]=a[i]%p,++dg[i];
                a[i]/=p;
            }
        }
        ull ans=0;
        for(int i=1;i<=n;++i){
            memset(cnt,0,sizeof(int)*(lim+5));
            int cur=1;
            for(int l=i;l;--l){
                bool cap=true;
                for(int k=0;k<dg[l];++k){
                    int pos=num[l][k][0],t=num[l][k][1];
                    if(cnt[pos]+t>=p){cap=false;break;}
                    cur=cur*ifac[cnt[pos]]%p;cnt[pos]+=t;
                    cur=cur*ifac[t]*fac[cnt[pos]]%p;
                }
                if(!cap) break;
                ans+=cur;
                // printf("%d %d %d
    ",i,l,cur);
            }
        }
        printf("%llu
    ",ans);
        return 0;
    }
    

    APIO2021 封闭道路 (CF1119F Niyaz and Small Degrees)

    题目

    APIO CF

    简答

    由于 (sum dleft(x ight)=2n-2) , 所以所有点需要被考虑的次数总和是 (mathcal{O}left(n ight)) 的,考虑优化单次复杂度

    (dp) 状态 (f_{i,0/1}) 表示是否会删和父节点那条边的最小代价

    那么每次 (dp) 的时候就是从 (f_{v,1}+e_{u,v}-f_{v,0},e_{u,v'}) 中选出最小的一些点再加上 (sum f_{v,0}) ,平衡树维护即可

    注意精细实现使得总复杂度 (mathcal{O}left(nlog n ight))

    代码

    #include <cstdio>
    #include <algorithm>
    #include <random>
    using namespace std;
    typedef long long ll;
    constexpr int N=250010,inf=1e9;
    constexpr ll linf=1e18;
    mt19937 rde;
    uniform_int_distribution<int> rnd(0,inf);
    struct Tree{
        int rd[N<<3],ch[N<<3][2],cnt[N<<3],sz;ll sum[N<<3],val[N<<3];
        void pushup(int u){cnt[u]=cnt[ch[u][0]]+cnt[ch[u][1]]+1,sum[u]=sum[ch[u][0]]+sum[ch[u][1]]+val[u];}
        void split(int u,int &x,int &y,int k){
            if(!u){x=y=0;return;}if(!k){y=u,x=0;return;}
            if(cnt[ch[u][0]]>=k) y=u,split(ch[u][0],x,ch[u][0],k);
            else x=u,split(ch[u][1],ch[u][1],y,k-cnt[ch[u][0]]-1);
            pushup(u);
        }
        void split_val(int u,int &x,int &y,ll k){
            if(!u){x=y=0;return;}
            if(val[u]<=k) x=u,split_val(ch[u][1],ch[u][1],y,k);
            else y=u,split_val(ch[u][0],x,ch[u][0],k);
            pushup(u);
        }
        int merge(int x,int y){
            if(!x || !y) return x|y;
            if(rd[x]<rd[y]){ch[x][1]=merge(ch[x][1],y);pushup(x);return x;}
            ch[y][0]=merge(x,ch[y][0]);pushup(y);return y;
        }
        ll query(int &u,int k){
            int t1=0,t2=0;split(u,t1,t2,k);
            // print(t1),print(t2);
            ll ret=sum[t1];u=merge(t1,t2);
            return ret;
        }
        void del(int &u,ll k,int gdb){
            // printf("deleting %d from %d
    ",k,gdb);
            // print(u);
            int t1=0,t2=0,t3=0,t4=0;split_val(u,t1,t2,k);
            // printf("fck
    ");
            split(t1,t3,t4,cnt[t1]-1);
            // printf("%d %d %d %d
    ",t1,t2,t3,t4);
            u=merge(t3,t2);
            // printf("comp
    ");
        }
        void insert(int &u,ll k,int gdb){
            // printf("inserting %d to %d
    ",k,gdb);
            ++sz;cnt[sz]=1;sum[sz]=val[sz]=k;rd[sz]=rnd(rde);
            int t1=0,t2=0;split_val(u,t1,t2,k);u=merge(t1,sz);u=merge(u,t2);
            // print(u);
        }
        void print(int u){
            printf("tree %d %d %d %lld %lld
    ",u,ch[u][0],ch[u][1],sum[u],val[u]);
            if(ch[u][0]) print(ch[u][0]);if(ch[u][1]) print(ch[u][1]);
        }
    }T;
    #define pr pair<int,int>
    #define fr first
    #define sd second
    vector<pr > es[N];int deg[N],n,rts[N];ll f[N][2];
    vector<int> pts[N];bool vis[N];
    void solve(int u,int fa,int wgt,int lim){
        // printf("%d %d %d %d
    ",u,fa,wgt,lim);
        if(fa) T.del(rts[u],wgt,u);int cur=0;ll sum=0;
        vis[u]=true;
        for(auto v:es[u]) if(v.fr!=fa){
            if(deg[v.fr]<=lim) break;
            solve(v.fr,u,v.sd,lim);int j=v.fr;
            T.del(rts[u],v.sd,u);
            if(f[j][1]+v.sd<=f[j][0]) ++cur,sum+=f[j][1]+v.sd;
            else T.insert(rts[u],f[j][1]+v.sd-f[j][0],u),sum+=f[j][0];
        }
        if(cur>=deg[u]-lim-1) f[u][1]=sum;
        else f[u][1]=sum+T.query(rts[u],deg[u]-lim-cur-1);
        // T.print(rts[u]);printf("%d cur:%d sum: %lld
    ",u,cur,sum);
        if(!lim && fa) f[u][0]=linf;
        else if(cur>=deg[u]-lim) f[u][0]=sum;
        else f[u][0]=T.query(rts[u],deg[u]-lim-cur)+sum;
        for(auto v:es[u]) if(v.fr!=fa){
            if(deg[v.fr]<=lim) break;int j=v.fr;
            T.insert(rts[u],v.sd,u);
            if(f[j][1]+v.sd>f[j][0]) T.del(rts[u],f[j][1]+v.sd-f[j][0],u);
        }
        if(fa) T.insert(rts[u],wgt,u);
    }
    int main(){
        scanf("%d",&n);
        for(int i=1,ta,tb,tc;i<n;++i) scanf("%d%d%d",&ta,&tb,&tc),T.insert(rts[ta],tc,ta),T.insert(rts[tb],tc,tb),
                                      es[ta].push_back(make_pair(tb,tc)),es[tb].push_back(make_pair(ta,tc)),++deg[ta],++deg[tb];
        for(int i=1;i<=n;++i) sort(es[i].begin(),es[i].end(),[](auto a,auto b){return deg[a.fr]>deg[b.fr];});
        for(int i=1;i<=n;++i) for(int j=0;j<deg[i];++j) pts[j].push_back(i);
        // printf("hey
    ");
        for(int i=0;i<n;++i){
            ll ans=0;
            for(auto j:pts[i]) if(!vis[j]) solve(j,0,0,i),ans+=f[j][0];
            for(auto j:pts[i]) vis[j]=false;
            printf("%lld ",ans);
        }
        printf("
    ");
        return 0;
    }
    

    LOJ 577 简单算术

    题目

    链接

    简答

    有个有趣的小结论,对于多项式 (Fleft(x ight),pin mathbb{P})(Fleft(x ight)^pequiv sum_{ige0} f_ix^{pi}pmod{p})

    有这个结论直接记忆化递归即可,复杂度 (mathcal{O}left(polyleft(n ight)polyleft(p ight)Q+pn^2 ight))

    代码

    #include <cstdio>
    #include <algorithm>
    #include <map>
    using namespace std;
    typedef long long ll;
    constexpr int N=60;
    int a[N],p,n,T,poly[N][N*N];
    map<pair<ll,ll>,int> mm;
    int solve(ll m,ll k){
        if(m<p) return poly[m][k];
        if(mm.count(make_pair(m,k))) return mm[make_pair(m,k)];
        ll g=m/p,r=m%p;int ret=0;
        for(int i=k%p;i<=r*n && i<=k;i+=p) ret=(ret+poly[r][i]*solve(g,(k-i)/p)%p)%p;
        mm[make_pair(m,k)]=ret;
        return ret;
    }
    int main(){
        scanf("%d%d",&n,&p);
        for(int i=0;i<=n;++i) scanf("%d",&a[i]),poly[1][i]=a[i];poly[0][0]=1;
        for(int i=2;i<p;++i) for(int j=0,l2=(i-1)*n;j<=n;++j) for(int k=0;k<=l2;++k) poly[i][j+k]=(poly[i][k+j]+a[j]*poly[i-1][k]%p)%p;
        scanf("%d",&T);
        while(T--){
            ll m,k;scanf("%lld %lld",&m,&k);
            printf("%d
    ",solve(m,k));
        }
        return 0;
    }
    

    IOI2019 矩形区域

    题目

    链接

    简答

    考虑每行每列中合法的区间,不妨设 (a_{l-1}ge a_{r+1})

    则位置 (k) 作为区间右端点 (+1) 时,左端点 (-1) 只会是 (argmax_{i}a_ige a_k) ,因此每行每列合法的区间数是 (mathcal{O}left(n ight))

    考虑此时所有行的合法区间,上下连着的左右端点相同的区间可以被扔到列上查询

    若此时行的合法区间上下左右边界是 (left[r_0,r_1 ight],left[c_0,c_1 ight]) ,则在 (c_1) 这一列查询这一列上能向左延申 (ge c_1-c_0+1) 的且 (subseteqleft[r_0,r_1 ight]) 的合法区间个数

    此时每一列上变成一个三维偏序,对所有区间和询问的延申长度排序就变为求当前有多少个区间 (subseteqleft[r_0,r_1 ight]) , 即二维偏序

    由于行的总合法区间个数是 (mathcal{O}left(nm ight)) 的,所以所有区间查询的 (sum r_1-r0+1)(mathcal{O}left(nm ight))

    所以直接暴力枚举右端点,求有多少个左端点 (ge r_0)(sum_{i=r_0}^{r_1}sum_{segs} left[lge r_0 ight]left[r=i ight]),直接建 (n) 棵树状数组维护,更新一个区间时在右端点那个后缀数组上修改,查询时对于 (left[l,r ight]) 每个线段树查询相加即可

    总复杂度 (mathcal{O}left(nmlog_2m ight))

    精细实现一下直接LOJ rk1

    代码

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <ctype.h>
    #include <vector>
    using namespace std;
    typedef long long ll;
    constexpr int N=2510,inf=1e9;
    int rl[N][N<<1],rr[N][N<<1],rcnt[N],rg[N][N<<1];bool rb[N][N<<1];
    int cl[N][N<<1],cr[N][N<<1],ccnt[N],cg[N][N<<1];bool cb[N][N<<1];
    struct qry{int l,r,x;};vector<qry> qls[N];
    int n,m,a0[N][N],a1[N][N],qtmp[N*N],ctmp[N<<1],qx0[N*N];
    int bt[N][N];
    void add(int *arr,int x,int v,int lim){for(;x<=lim;x+=(x&(-x))) arr[x]+=v;}
    int query(int *arr,int x){int ret=0;for(;x;x-=(x&(-x))) ret+=arr[x];return ret;}
    void bkt_sort(int *res,int *nums,int t,int lim){
        static int cnt[N];memset(cnt,0,sizeof(int)*(lim+2));
        for(int i=1;i<=t;++i) ++cnt[nums[i]];
        for(int i=lim-1;i;--i) cnt[i]+=cnt[i+1];
        for(int i=t;i;--i) res[cnt[nums[i]]--]=i;
    }
    struct stck{
        int dt[N<<1],cnt;bool ity(){return cnt==0;}int top(){return dt[cnt];}void push(int val){dt[++cnt]=val;}void pop(){--cnt;}void init(){cnt=0;}
    }f1;
    namespace fr{
        char buf[1<<23],obuf[1<<23],*p1=buf,*p2=buf,*O=obuf;
        #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
        template<typename T>
        void rd(T &res){
            bool flag=false;T ret=0;static char c;c=getchar();
            while(!isdigit(c) && c!='-') c=getchar();
            // printf("%c
    ",c);
            if(c=='-') flag=true,c=getchar();while(isdigit(c)) ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();
            res=flag?-ret:ret;
        }
        template<typename T>
        void write(T k){
            if(k<0) *O++=='-';
            if(k>=10) write(k/10);
            *O++=k%10+'0';
        }
        void flush(){
            fwrite(obuf,O-obuf,1,stdout);
        }
    }
    int main(){
        // freopen("ioi.in","r",stdin);
        fr::rd(n),fr::rd(m);
        // printf("%d %d
    ",n,m);
        for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) fr::rd(a0[i][j]),a1[j][i]=a0[i][j];
        for(int i=1;i<=n;++i){
            f1.init();f1.push(1);
            for(int j=2;j<=m;++j){
                int cur;
                while(!f1.ity() && a0[i][cur=f1.top()]<a0[i][j]){
                    if(cur<j-1) ++rcnt[i],rl[i][rcnt[i]]=cur+1,rr[i][rcnt[i]]=j-1;
                    f1.pop();
                }
                if(!f1.ity()){
                    cur=f1.top();
                    if(cur<j-1) ++rcnt[i],rl[i][rcnt[i]]=cur+1,rr[i][rcnt[i]]=j-1;
                    if(a0[i][cur]==a0[i][j]) f1.pop();
                }
                f1.push(j);
            }
            for(int j=1,k=1;j<=rcnt[i];++j){
                while(k<=rcnt[i-1] && ((rr[i-1][k]<rr[i][j]) || (rr[i-1][k]==rr[i][j] && rl[i-1][k]>rl[i][j]))) ++k;
                if(rr[i-1][k]==rr[i][j] && rl[i-1][k]==rl[i][j]) rg[i][j]=rg[i-1][k]+1,rb[i-1][k]=true;
                else rg[i][j]=1;
            }
        }
        for(int i=1;i<=n;++i){
            for(int j=1,a,b,c;j<=rcnt[i];++j) if(!rb[i][j]){
                a=i-rg[i][j]+1,b=rr[i][j]-rl[i][j]+1,c=rr[i][j];
                qls[c].push_back((qry){a,i,b});
            }
        }
        for(int i=1;i<=m;++i){
            f1.init();f1.push(1);
            for(int j=2;j<=n;++j){
                int cur;
                while(!f1.ity() && a1[i][cur=f1.top()]<a1[i][j]){
                    if(cur<j-1) ++ccnt[i],cl[i][ccnt[i]]=cur+1,cr[i][ccnt[i]]=j-1;
                    f1.pop();
                }
                if(!f1.ity()){
                    cur=f1.top();
                    if(cur<j-1) ++ccnt[i],cl[i][ccnt[i]]=cur+1,cr[i][ccnt[i]]=j-1;
                    if(a1[i][cur]==a1[i][j]) f1.pop();
                }
                f1.push(j);
            }
            for(int j=1,k=1;j<=ccnt[i];++j){
                while(k<=ccnt[i-1] && ((cr[i-1][k]<cr[i][j]) || (cr[i-1][k]==cr[i][j] && cl[i-1][k]>cl[i][j]))) ++k;
                if(cr[i-1][k]==cr[i][j] && cl[i-1][k]==cl[i][j]) cg[i][j]=cg[i-1][k]+1,cb[i-1][k]=true;
                else cg[i][j]=1;
            }
        }
        int ans=0;
        for(int i=1;i<=m;++i){
            int h=qls[i].size();for(int j=1;j<=h;++j) qx0[j]=qls[i][j-1].x;
            bkt_sort(qtmp,qx0,h,m);bkt_sort(ctmp,cg[i],ccnt[i],m);int k=0;
            for(int j=1;j<=h;++j){
                int t=qtmp[j];
                while(k<ccnt[i] && cg[i][ctmp[k+1]]>=qx0[t]) ++k,add(bt[cr[i][ctmp[k]]],n-cl[i][ctmp[k]]+1,1,n);
                for(int l0=qls[i][t-1].l,r0=qls[i][t-1].r,h=l0;h<=r0;++h) ans+=query(bt[h],n-l0+1);
            }
            for(int j=1;j<=k;++j) add(bt[cr[i][ctmp[j]]],n-cl[i][ctmp[j]]+1,-1,n);
        }
        // printf("%d %d %d
    ",n,m,ans);
        fr::write(ans);
        fr::flush();
        return 0;
    }
    
  • 相关阅读:
    MyEclipse快捷键大全
    The type 'Microsoft.Office.Interop.Excel.ApplicationClass' has no constructors defined
    ‘Microsoft.Office.Interop.Excel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c’
    使用游标循环表的简单DEMO
    URL Records
    一步一步教你实现CTreeCtrl 自绘
    自绘按钮
    UDP文件传输的实现
    美化VC界面(用户登录界面)
    如何截取QQ密码和聊天内容、去掉QQ广告栏、添加QQ尾巴
  • 原文地址:https://www.cnblogs.com/aixiaoyaowudi/p/15483590.html
Copyright © 2011-2022 走看看