zoukankan      html  css  js  c++  java
  • 【洛谷】xht模拟赛 题解

    前言

    大家期待已久并没有的题解终于来啦~
    这次的T1和HAOI2016撞题了...深表歉意...表示自己真的不知情...

    天下的水题总是水得相似,神题各有各的神法。——《安娜·卡列妮娜》

    出题的过程:@PhoenixEclipse @xht13127提供了一些想法,我负责写标程,对拍,造数据...然后 @SakuraDance帮我验了题。感谢上述,以及其他几位dalao的资辞。

    题解

    T1

    和HAOI2016撞车 http://www.lydsy.com/JudgeOnline/problem.php?id=4562

    30分做法:直接爆搜,注意取模。

    100分做法:topo排序然后简单dp,或者直接爆搜加个记忆化...(如果是记忆化,记得不要用0来表示未访问的点QwQ)

    T2

    wyw神犇提出了一种(O(sqrt N))的做法,太神啦!详见:http://blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/60475362

    一道披着几何外衣的数学题~

    问题可以转化为,对 (ale ile b) ,所有 (xyz=i) 的所有整数解((x,y,z)) , 求((|x|+|y|+|z|)^2) 的和。

    由于对称性,所以我们只需要算第一卦限((x,y,z>0))的答案,再乘4就行啦。

    30分做法:暴力枚举(i),再暴力枚举满足(xyz=i)(x,y,z),然后暴力计算答案。

    60分做法

    首先,我们可以用([1,b]) 的答案减去 ([1,a-1]) 的答案。

    然后注意到((x+y+z)^2=(x^2+y^2+z^2)+2(xy+yz+zx)) , 其中两个括号里的三项都是对称式。于是我们可以单独算出所有的(x^2)(yz) 对答案的贡献,再把前者加上后者乘2。

    接下来推式子...公式恐惧症请做好心理准备

    (sigma(n)) 等于n的约数个数;

    [egin{aligned} \ans1=&sum_{i=1}^Nsum_{xyz=i}x^2 \=&sum_{i=1}^Nsum_{x|i}x^2sum_{yz=frac i x} \=&sum_{i=1}^Nsum_{x|i}x^2sigma(frac i x) \=&sum_{x=1}^N x^2sum_{x|i}sigma(frac i x) \=&sum_{x=1}^N x^2sum_{dle lfloor N/x floor}sigma(d) end{aligned} ]

    [egin{aligned} \ans2=&sum_{i=1}^Nsum_{xyz=i}yz \=&sum_{i=1}^Nsum_{x|i}sum_{yz=frac i x}yz \=&sum_{i=1}^Nsum_{x|i}frac i xsigma(frac i x) \=&sum_{i=1}^Nsum_{x|i}xsigma(x) \=&sum_{x=1}^N xsigma(x)lfloor frac N x floor end{aligned} ]

    (ans=4*(ans1+2*ans2))

    对于ans1,因为(lfloor frac N x floor) 只有(sqrt N) 种取值, 我们可以用一种经典的方法分段计算,再对后面的前缀和预处理出来,或者暴力求和,这样可以做到O(N)。

    对于ans2,同样分段计算,再预处理(xsigma(x)) 的前缀和,也是O(N)。
    总复杂度:O(N)。如果你喜欢用一种NlgN的方法筛约数个数...那就是O(NlgN)。

    现场有一位大爷用了筛法也过了60分,orz。

    100分做法

    在60分做法的基础上,用杜教筛求(sigma(x))(xsigma(x)) 的前缀和,复杂度O(N^(2/3))。
    详见唐老师博客:http://blog.csdn.net/skywalkert/article/details/50500009

    T3

    被喷是裸的数据结构题了啊...

    观察题目中的要求,将变量看做点,方程看做边,只会形成一棵树。

    30分做法:因为只有询问操作,我们先读入所有数据,建一棵树,每条边用两条有向边表示,权值分别为c和-c,然后dfs一遍,预处理出lca(或者你要树剖也行),然后处理询问。

    100分做法:发现只要用LCT维护就行了。几个细节:边权不好维护,那么我们就插入虚拟的点来表示边。在询问一条链的时候,要求链上的方程是x1-x2=a, x2-x3=b, x3-x4=c...这样的形式,即要求符号的方向相同。我们可以让每条边上的方程表示子节点减父节点的值,然后换根的时候,发现只有新的根到原来的根的路径上的顺序要颠倒,于是在access的splaytree里面打一个tag取反。具体实现见代码。

    代码

    T1

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN=1e5+5, MD=10007;
    int in[MAXN], deg[MAXN], to[MAXN][10], K[MAXN], N;
    int p[MAXN], M, f[MAXN], ans;
    int read()
    {
        int r=0, c=getchar();
        while(c<'0'||'9'<c) c=getchar();
        while('0'<=c&&c<='9') r=r*10+c-'0', c=getchar();
        return r;
    }
    int main()
    {
        scanf("%d", &N);
        for(int i=1; i<=N; ++i){
            K[i]=read();
            for(int j=0; j<K[i]; ++j) in[to[i][j]=read()]++;
        }
        for(int i=1; i<=N; ++i)
            if(!in[i]) f[p[M++]=i]=1;
        for(int i=0; i<M; ++i){
            int u=p[i];
            for(int j=0; j<K[u]; ++j){
                int v=to[u][j];
                if(!(--in[v])) p[M++]=v;
            }
        }
        for(int i=0; i<M; ++i){
            int u=p[i];
            for(int j=0; j<K[u]; ++j){
                int v=to[u][j];
                f[v]=(f[v]+f[u])%MD;
            }
            if(!K[u]) ans=(ans+f[u])%MD;
        }
        printf("%d
    ", ans);
        return 0;
    }
    

    T2

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN = 5e6+5, MD=10007;
    int pr[MAXN], f[MAXN], g[MAXN], np, M;
    short ip[MAXN];
    void pre()
    {
        ip[1]=f[1]=1;
        for(int i=2; i<M; ++i){
            if(!ip[i]) pr[np++]=i, f[i]=2, g[i]=1;
            for(int j=0, k, p; j<np; ++j){
                p=pr[j], k=i*p;
                if(k>=M) break;
                ip[k]=1;
                if(i%p==0){
                    f[k]=f[i]/(g[i]+1)*(g[i]+2);
                    g[k]=g[i]+1;
                    break;
                }
                f[k]=f[i]*2;
                g[k]=1;
            }
        }
        for(int i=1; i<M; ++i){
            f[i]%=MD;
            g[i]=(i*f[i]+g[i-1])%MD;
            f[i]=(f[i]+f[i-1])%MD;
        }
    }
    int F(int n)
    {
        if(n<M) return f[n];
        int r=0;
        for(int i=1, j; i<=n; i=j+1){
            int t=n/i; j=n/t;
            r=(r+(ll)(j-i+1)*t)%MD;
        }
        return r;
    }
    int G(int n)
    {
        if(n<M) return g[n];
        int r=0;
        for(int i=1, j; i<=n; i=j+1){
            int t=n/i; j=n/t;
            r=(r+((ll)(i+j)*(j-i+1)/2%MD)*t*(t+1)/2)%MD;
        } return r;
    }
    int work(int n)
    {
        int l=0, c=0, r1=0, r2=0;
        for(ll i=1, j; i<=n; i=j+1){
            j=n/(n/i); c=(ll)j*(j+1)%MD*(2*j+1)*1668%MD;
            r1=(r1+(ll)(c-l+MD)*F(n/i))%MD; l=c;
        }
        l=0;
        for(ll i=1, j; i<=n; i=j+1){
            j=n/(n/i); c=G(j);
            r2=(r2+(ll)(c-l+MD)*(n/i))%MD; l=c;
        }
        return (r1+r2*2)*12%MD;
    }
    int main()
    {
        int a, b;
        scanf("%d%d", &a, &b);
        for(M=0; M*M*M<=b; M++);
        M=min(MAXN, M*M);
        pre();
        printf("%d
    ", (work(b)-work(a-1)+MD)%MD);
        return 0;
    }
    

    T3

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN = 3e5+5, MD=6662333;
    struct Node
    {
        Node *ch[2], *fa;
        int rev, inv, w, sum;
    }tr[MAXN], *nil=tr;
    inline void init(int x)
    {
        tr[x].ch[0]=tr[x].ch[1]=tr[x].fa=nil;
    }
    inline void up(Node *x)
    {
        Node *l=x->ch[0], *r=x->ch[1];
        x->sum=x->w+l->sum+r->sum;
    }
    inline void down(Node *x)
    {
        Node *l=x->ch[0], *r=x->ch[1];
        if(x->inv){
            l->inv^=1; l->sum=-l->sum; l->w=-l->w;
            r->inv^=1; r->sum=-r->sum; r->w=-r->w;
            x->inv=0;
        }
        if(x->rev){
            l->rev^=1; r->rev^=1;
            swap(x->ch[0], x->ch[1]);
            x->rev=0;
        }
    }
    inline int isrt(Node *x){return !(x->fa->ch[0]==x||x->fa->ch[1]==x);}
    void rot(Node *x)
    {
        Node *y=x->fa, *z=y->fa; int l=y->ch[1]==x, r=!l;
        if(!isrt(y)) z->ch[z->ch[1]==y]=x;
        x->ch[r]->fa=y; y->fa=x; x->fa=z;
        y->ch[l]=x->ch[r]; x->ch[r]=y;
        up(y); up(x);
    }
    Node *st[MAXN]; int top;
    void splay(Node *x)
    {
        st[top=0]=x;
        for(Node *u=x; !isrt(u); u=u->fa) st[++top]=u->fa;
        while(top>=0) down(st[top--]);
        while(!isrt(x)){
            Node *y=x->fa, *z=y->fa;
            if(!isrt(y)) rot((z->ch[1]==y)^(y->ch[1]==x)?x:y);
            rot(x);
        }
    }
    void access(Node *x)
    {
        for(Node *t=nil; x!=nil; t=x, x=x->fa)
            splay(x), x->ch[1]=t, up(x);
    }
    void mkrt(Node *x)
    {
        access(x); splay(x);
        x->rev^=1; x->inv^=1; x->sum=-x->sum; x->w=-x->w;
    }
    void lnk(Node *x, Node *y)
    {
        mkrt(x); x->fa=y;
    }
    void cut(Node *x, Node *y)
    {
        mkrt(x); access(y); splay(y);
        x->fa=y->ch[0]=nil;
    }
    Node *getrt(Node *x)
    {
        while(x->fa!=nil) x=x->fa;
        return x;
    }
    int N, M, K, Q, ne;
    inline int xht(int x){return x>0?x%K:(K-(-x)%K)%K;}
    int ha[MAXN], hb[MAXN], he[MAXN], nxt[MAXN], hd[MD], nh=1;
    void add(int a, int b, int e)
    {
        int k=((ll)a*b+a+b)%MD;
        ha[nh]=a; hb[nh]=b; he[nh]=e; nxt[nh]=hd[k]; hd[k]=nh++;
    }
    int del(int a, int b)
    {
        int k=((ll)a*b+a+b)%MD;
        for(int i=hd[k], j=0; i; i=nxt[i], j=i){
            if((ha[i]==a&&hb[i]==b)||(ha[i]==a&&hb[i]==b)){
                if(!j) hd[k]=nxt[i];
                else nxt[j]=nxt[i];
                return he[i];
            }
        }
        return 0;
    }
    int main()
    {
        scanf("%d%d%d%d", &N, &M, &K, &Q);
        ne=N+1;
        for(int i=0; i<=N+M+Q; ++i) init(i);
        for(int i=0; i<M; ++i){
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
        //    G[a][b]=G[b][a]=ne;
            add(a, b, ne);
            tr[ne].w=tr[ne].sum=c;
            lnk(tr+a, tr+ne); lnk(tr+ne, tr+b);
            ne++;
        }
        for(int i=0; i<Q; ++i){
            int t, a, b, c;
            scanf("%d%d%d", &t, &a, &b);
            if(t==1){
                scanf("%d", &c);
            //    G[a][b]=G[b][a]=ne;
                add(a, b, ne);
                tr[ne].w=tr[ne].sum=c;
                lnk(tr+a, tr+ne); lnk(tr+ne, tr+b);
                ne++;
            }else if(t==2){
            //    int e=G[a][b];
                int e=del(a, b);
                if(e){
                    cut(tr+a, tr+e); cut(tr+e, tr+b);
                //    G[a][b]=G[b][a]=0;
                }
            }else{
                scanf("%d", &c);
                if(getrt(tr+a)==getrt(tr+b)){
                    mkrt(tr+a); access(tr+b); splay(tr+b);
                    printf("%d
    ", xht(c-tr[b].sum));
                }else puts("-1");
            }
        }
        return 0;
    }
    
  • 相关阅读:
    2020-08-11 题目题解
    N皇后问题
    逆序对(模板)
    归并选择(模板)
    快速选择(模板)
    快速排序(模板)
    vuecli4+elementui实现面包屑
    vue-路由导航(守卫)那些事
    vue-vant中ImagePreview 图片预览正确的打开方式
    Selenium 对表格table处理
  • 原文地址:https://www.cnblogs.com/will7101/p/6506724.html
Copyright © 2011-2022 走看看