• [校内模拟题3]


    YSG

    (1s,64MB,ysg.in,ysg.out)
    描述
    ysg,yxy,azw 三人正在刷题。
    他们每做一题的时间都是一个有理数。如果在某一时刻,三人同时做完一道
    题,那么,他们会开始谈笑风生。
    现在,他们想知道,从时刻 0 开始,至少要等多久才能谈笑风生。
    输入格式
    一行 6 个整数 a1,b1,a2,b2,a3,b3,其中 ysg 每做一道题的时间是 a1/b1,yxy
    是 a2/b2,azw 是 a3/b3。不保证 a,b 互质。
    输出格式
    一行 2 个数 c,d,表示第一次谈笑风生是在时刻 c/d。其中 c,d 互质。
    输入样例
    3 6 4 5 3 1
    输出样例
    12 1
    样例解释
    在时刻 12,ysg 做了 24 道题,yxy 做了 15 道题,azw 做了 4 道题,他们开始
    谈笑风生。
    备注
    对于 30%的数据,b1=b2=b3=1。
    对于 100%的数据,a1,a2,a3,b1,b2,b3<=100
    ————————————————————————————————————————

    大水题。就是求a1*b2*b3,a2*b1*b3,a3*b1*b2的最小公倍数除以b1*b2*b3,注意开long long。

    code

    #include <cstdio>
    #include <iostream> 
    using namespace std;
     
    typedef long long LL;
    LL a1, a2, a3, b1, b2, b3;
    
    LL gcd(LL a, LL b) {
        if (b == 0) return a;
        else return gcd(b, a % b);
    }
    
    LL lcm(LL a, LL b) {
        return a * b / gcd(a, b);
    }
    
    void slove1() {
        cout << lcm(lcm(a1, a2), a3) << ' ' << '1' << endl;
    }
    
    int main() {
        freopen("ysg.in", "r", stdin);
        freopen("ysg.out", "w", stdout);
        cin >> a1 >> b1 >> a2 >> b2 >> a3 >> b3;
        if (b1 == 1 && b2 == 1 && b3 == 1) slove1();
        else {
        LL fm = lcm(lcm(b1, b2), b3);
        a1 *= (fm / b1);
        a2 *= (fm / b2);
        a3 *= (fm / b3);
        LL llccmm = lcm(lcm(a1, a2), a3);
        LL ggccdd = gcd(llccmm, fm);
        cout << llccmm / ggccdd << ' ' << fm / ggccdd << endl;
        }
        return 0; 
    }

    YXY

    (1s,128MB,yxy.in,yxy.out)
    问题描述
    ysg,yxy,azw 三人正在刷题。
    OJ 上一共有 n 道题,然而因为有的题的算法有人不会,所以他们决定每人做
    一道题。
    现在他们已经分配好了做题任务,把每道题都指定给一个人做。但是如果有
    人做的题数量比其他人多,他就会感觉不爽。
    为了避免这种情况,他们只好选一些题不做,但是为了美观,他们做的题目
    必须是连续一段。
    他们现在想知道,有多少种选择的方案满足条件。
    输入格式
    输入数据有 n 行,每行一个字符串,为”ysg”,”yxy”,”azw”中的一个,第 i 行代
    表做第 i 题的人的名字。
    输出格式
    输出有多少种选择的方案
    输入样例
    ysg
    yxy
    azw
    azw
    yxy
    azw
    yxy
    ysg
    yxy
    ysg
    ysg
    输出样例
    3
    样例说明
    有三种方案,选 1~3 一段,选 6~8 一段,选 3~11 一段,每一段内三个名字
    出现次数一样多
    数据范围
    对于 20%的数据,n <= 100。
    对于 40%的数据,n <= 1000。
    对于 100%的数据,n <= 100000

    ————————————————————————————————————————

    需要一点巧妙的方法。

    对每个位置记录以它为结尾的前缀里面,第一类题减第二类题有多少道,第二类题减第三类题有多少道,如果两个位置的这两个值分别都相等,那这两个位置之间的连续一段就可行。

    开map[x][y]记当前这个位置以前的,这两个值分别为x,y的位置有多少个,然后新加入一个位置就把ans+=map[x][y],然后map[x][y]++;注意处理下标。

    map是红黑树动态开点,空间很玄学,注意把握。

    #include<cstdio>
    #include<map>
    using namespace std;
    
    map<int, int> a[200001];
    
    int main() {
        long long ans = 0;
        a[100000][100000]++;
        for (int x = 0, y = 0; ; ) {
            char s[10];
            if (scanf("%s", s) == -1) break;
            //设第一类题减第二类题有x道
            //第二类题减第三类题有y道
            //记ysg第一类,yxy第二类,azw第三类 
            else if (s[0] == 'a') y--;
            else if (s[0] == 'y' && s[1] == 'x') y++, x--;
            else x++;
            ans += a[x + 100000][y + 100000]++;
        }
        printf("%lld
    ",ans);
    }

    AZW

    (2s,256MB,azw.in,azw.out)
    问题描述
    ysg,yxy,azw 三人正在刷题。
    在他们刷题的 oj 上,除第 0 题外,每道题都有一个父亲(父亲的编号不一定
    比他自己小),所以可以把这些题看成一个树形的结构。
    他们在接下来的 m 天中,每天要么做一道新题,要么浏览一些题。浏览的方
    法是:先选两道题 x,y,然后把这两道题之间路径上的题全部浏览一遍。
    在浏览的时候,他们有时会发现一道题似曾相识,可又想不起何时做过,原
    来是他们做这道题的时间太过久远,以致记忆的模糊。具体来说,如果一道题做
    题时间距离浏览的时间已经超过了他们当天的记忆力,他们就会出现这样的情况
    (具体看样例)。
    所以,现在对于每次浏览,他们想知道,这次浏览会看多少道题,以及有多
    少题似曾相识。
    输入格式
    第一行一个数 n,代表 oj 上有 n+1 题(从 0 到 n)。
    第二行 n 个数,表示第 i 道题的父亲的编号。
    第三行一个数 m,代表一共 m 天
    接下来 m 行,先是一个数 1 或者 2,如果是 1,后面接三个数 x,y,c,表示当
    天的浏览从 x 到 y,当天记忆力为 c,否则后面接一个数 x,表示当天做了第 x 题
    输出格式
    对于每次浏览,输出一行两个数,表示这次浏览会看多少道题,以及有多少
    题似曾相识。
    输入样例
    7
    0 1 1 2 2 3 3
    6
    1 4 7 0
    2 1
    2 4
    2 7
    1 4 7 1
    1 4 7 3
    输出样例
    5 0
    5 2
    5 1
    样例说明
    3 次都是浏览 5 道题,分别是 4 号、2 号、1 号、3 号和 7 号。其中,对于第
    1 天,所有题都没有做过;对于第 5 天,有 2 道题似曾相识,分别是 1 号和 4 号,
    7 号虽然做过,但只隔 1 天,刚好等于记忆力,所以还想得起;对于第 6 天,只
    有 1 号似曾相识。
    数据范围
    对于 20%的数据,n <= 100,m<=100。
    对于 40%的数据,n <= 2000,m<=2000。
    另有 20%的数据,没有做题的操作
    另有 20%的数据,虽有做题操作,但每天的记忆力都是 0
    对于 100%的数据,n <= 200000,m<=200000,保证一道题不会做两遍

    ————————————————————————————————————————

    爆零。。。

    是道难题。废话

    贴上标程和官方题解

    第三题:
        40分就暴力在树上走,每路过一个点就看他是不是之前做过,是在什么时候做的。
        60分就是40分加上求lca,
        80分就是60分加上每次把一个点的权值加一,然后询问一条链上所有点的权值和,可以用dfs序,或者树链剖分,做法很多。
        100分就是相当于先把每个点是在什么时候被做的预处理出来,然后每次询问一条链上面被做时间小于一个给定数的点有多少个,用树上主席树。

    code

    #include<cstdio>
    #include<vector>
    #include<algorithm>
    #define N 200011
    using namespace std;
    
    int n,m,a1[N],a2[N],a3[N],a4[N];
    int cc[N],c[20*N][2],z[20*N],t;
    vector<int>g[N];
    int s[N],f[20][N],d[N];
    int lca(int u,int v){
        if(d[u]<d[v])swap(u,v);
        for(int i=17;~i;i--)
            if(1<<i<=d[u]-d[v])
                u=f[i][u];
        if(u==v)return u;
        for(int i=17;~i;i--)
            if(f[i][u]^f[i][v])
                u=f[i][u],v=f[i][v];
        return f[0][u];
    }
    int cx(int o,int p){
        int ans=0;
        for(int l=1,r=m;l^r;)
            if(p>l+r>>1)ans+=z[c[o][0]],
                o=c[o][1],l=(l+r>>1)+1;
            else o=c[o][0],r=l+r>>1; 
        return ans;
    }
    int main(){
        scanf("%d",&n);
        t=++n;
        for(int i=2;i<=n;i++){
            scanf("%d",&f[0][i]);
            g[++f[0][i]].push_back(i);
        }
        scanf("%d",&m);
        for(int i=1;i<=n;i++)
            cc[i]=m;
        for(int i=1;i<=m;i++){
            scanf("%d",&a1[i]);
            if(a1[i]==1)scanf("%d%d%d",&a2[i],&a3[i],&a4[i]),a2[i]++,a3[i]++;
            else scanf("%d",&a2[i]),cc[++a2[i]]=i;
        }
        d[1]=1;
        for(s[s[0]=1]=1;s[0];){
            int u=s[s[0]--];
            z[u]=z[f[0][u]]+1;
            for(int o=u,oo=f[0][u],l=1,r=m;l^r;){
                int w=cc[u]>l+r>>1;
                if(w)l=(l+r>>1)+1;
                else r=l+r>>1;
                c[o][w^1]=c[oo][w^1];
                o=c[o][w]=++t,oo=c[oo][w];
                z[o]=z[oo]+1;
            }
            for(int i=0;i<g[u].size();i++)
                d[g[u][i]]=d[u]+1,s[++s[0]]=g[u][i];
        }
        for(int k=0;1<<k<n;k++)
            for(int i=1;i<=n;i++)
                f[k+1][i]=f[k][f[k][i]];
        for(int i=1;i<=m;i++)
            if(a1[i]==1){
                int u=a2[i],v=a3[i],w=lca(u,v),p=i-a4[i];
                printf("%d %d
    ",d[u]+d[v]-2*d[w]+1,cx(u,p)+cx(v,p)-cx(w,p)-cx(f[0][w],p));
            }
    }

     然后我们的rqy说

    然后就有了dfs序+LCA+树状数组的写法

  • 相关阅读:
    Windows Service 2016 DatacenterStandEmbedded激活方法
    批处理文件设置IP以及DNS
    C#类的一些基础知识(静态方法可以不用实例化调用)
    Dynamics Crm Plugin插件注册的问题及解决方案(持续更新。。。。。。)
    【转载】C# get 与set的一些说明
    C#补位函数PadLeft和PadRight
    Kubernetes集群调度之Scheduler
    Kubernetes集群控制之ControllerManager
    Kubernetes集群大脑之apiserver
    Kubernetes集群存储之etcd
  • 原文地址:https://www.cnblogs.com/hkttg/p/9385408.html
Copyright © 2020-2023  润新知