zoukankan      html  css  js  c++  java
  • 洛谷 P4964 绫小路的特别考试 解题报告

    P4964 绫小路的特别考试

    题目背景

    这世界上「胜利」便是一切。无关乎过程。
    要付出多少牺牲都无所谓。只要最后我「胜出」那就行了。

    题目描述

    一场新的特别考试来临了,这次的考试内容是(wan e de)文化课,但有所不同的是,考试中允许学生使用对讲机。然而,对讲机的接收范围有限(每个对讲机都能发送无限远,但是只能接收到接收范围内的信号),所以,需要周密地安排才能将对讲机的功效发挥到最大。这时,绫小路找到了你,希望你能帮他计算某种方案下的结果。

    考试时,共有 (n) 名学生坐成一排(从左至右依次编号为 (1) ~ (n)),绫小路自己坐在第 (c) 号位置。每名学生都有一个能力值 (w_i)。绫小路已经给每名学生安排了一个接收范围为 (d_i) 的对讲机。

    每名学生可以直接做出难度不超过自身能力值的所有题目,一旦一名学生凭能力做出某道题,他就会把这道题的做法进行广播。一名坐在位置 (i),有接收范围为 (d_i) 的对讲机的学生,可以接收到 ([i-d_i, i+d_i]) 范围内所有学生的广播,若这个范围内有人公布了做法,则他将会做这道题,并也会把这道题的做法进行广播。

    绫小路会问你一些问题:当一道题目难度为 (x) 时,有多少学生会做这道题?由于绫小路想隐藏实力,他可能会修改自己的能力值。这两种操作分别用以下两种方式表示:

    • (1 x),表示询问当一道题目难度为 (x) 时,有多少学生会做这道题。
    • (2 x),将绫小路的能力值修改为 (x),即将 (w_c) 修改为 (x)

    形式化描述(与上文同义):

    给你两个长为 (n) 的数列 (w_{1..n})(d_{1..n}),以及一个 (w_c) 可修改的位置 (c)。现在有两种操作:

    • (1 x) 表示一次询问:设 (f_i=egin{cases}1quad(w_ige x)\1quad(exists i-d_ile jle i+d_i,f_j=1)\ 0quad(otherwise)end{cases}),这里的 (f_i) 定义中引用了 (f_j),$ $所以 (f_{1..n}) 是会不断更新的,直到无法继续更新时,计算这次询问的答案为 (sumlimits_{i=1}^nf_i)
    • (2 x) 表示一次修改:把 (w_c) 修改为 (x)

    输入输出格式

    输入格式

    由于数据量太大,为了避免读入耗时过长,使用伪随机数生成器的方式输入,并强制在线

    建议你忽略输入格式,直接使用下面提供的数据生成器模板,了解具体的生成过程对你来说是不必要的。

    第一行,三个正整数 (n, m, c),分别代表学生的总人数,操作总数和绫小路所在的位置。

    第二行,五个整数 (mathrm{seed}, mathrm{mind}, mathrm{maxd}, mathrm{mfq}, k)

    此处的 (mathrm{mind}, mathrm{maxd}) 仅用于生成初始的 (d_{1..n}),下文中调整 (d_p) 所用的 (t) 可能不在 ([mathrm{mind}, mathrm{maxd}]) 范围内。

    接下来的 (k) 行,每行两个整数 (p, t),表示调整第 (p) 号同学的对讲机接收范围(即 (d_p))为 (t)

    若一名同学的对讲机接收范围被调整多次,以最后一次为准。


    ** 数据生成器模板:**

    #include <cstdio>
    
    unsigned long long seed;
    int n, m, c, mfq, mind, maxd, k, w[2000001], d[2000001];
    
    inline int randInt() { seed = 99999989 * seed + 1000000007; return seed >> 33; } 
    
    void generate()
    {
    	// 在读入种子后生成初始的 w 数组和 d 数组.
        for (int i = 1; i <= n; i++) { w[i] = randInt() % n; }
        for (int i = 1; i <= n; i++) { d[i] = randInt() % (maxd - mind + 1) + mind; }
    }
    
    void getOperation(int lastans, int &opt, int &x)
    {
        // 生成一次操作的参数 opt 和 x, lastans 表示上一次询问的答案, 初始值为 0.
        if ((0ll + randInt() + lastans) % mfq) { opt = 1; } else { opt = 2; }
        x = (0ll + randInt() + lastans) % n;
    }
    
    int main()
    {
        scanf("%d %d %d", &n, &m, &c);
        scanf("%llu %d %d %d %d", &seed, &mind, &maxd, &mfq, &k);
        generate();
        for (int i = 1; i <= k; i++)
        {
            int p, t;
            scanf("%d %d", &p, &t);
            d[p] = t;
        }
        // 从这里开始,你可以使用生成的 w 数组和 d 数组.
        int lastans = 0, finalans = 0;
        for (int i = 1; i <= m; i++)
        {
            int opt, x;
            getOperation(lastans, opt, x);
            if (opt == 1)
            {
                // 在这里执行询问操作,并用答案的表达式替代下面的 ___.      
                finalans = (finalans * 233ll + ___) % 998244353;
                lastans = ___;
            }
            else
            {
                // 在这里执行修改操作.
            }
        }
        printf("%d
    ", finalans);
        return 0;
    }
    

    输出格式

    (ans_i) 表示第 (i) 次询问(操作 (1) )的答案,(T_i=egin{cases}(T_{i-1} imes 233+ans_i)mod 998244353quad(ige 1)\0quad(i=0)end{cases})

    (q) 表示询问(操作 (1) )的个数,你只需要输出一个整数 (T_q)

    说明

    你需要用到的变量:

    (1le cle nle 2 imes 10^6)(1le mle 2 imes 10^6)(0le w_i, d_i, x<n)

    其它用于生成数据的变量:

    (1le mathrm{seed}, mathrm{mfq}le 10^9)(0le mathrm{mind}le mathrm{maxd}<n)(0le kle 2 imes 10^5)(1le ple n)(0le t<n)


    思路很简单但是为什么想不出来的一个题目??

    考虑暴力,每个点向可以接收到( t{Ta})的点连有向边,这样从每个点出发可以遍历到的点都是( t{Ta})可以把答案传出去的点。这样连边是(O(n^2))

    考虑优化连边,发现找离它最近左右的可以连到( t{Ta})的点就可以了。

    实现的时候拿个栈就可以维护了。

    发现修改每次只改一个位置的值,可以特殊处理( t{Ta})

    预处理出每个询问在路哥传不传答案时的贡献。

    可以把元素排序以后从大到小进行( t{dfs}),并每次记录被染色的点。

    排序可以桶排,计数排序保证复杂度,也可以直接( t{sort}),跑的也挺快的。


    Code:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    unsigned long long seed;
    const int N=2e6+10;
    int n,m,c,mfq,mind,maxd,k,d[N],ans[2][N];
    struct node
    {
        int w,id;
        bool friend operator <(node n1,node n2){return n1.w>n2.w;}
    }w[N];
    inline int randInt(){seed=99999989*seed+1000000007;return seed>>33;}
    void generate()
    {
        for(int i=1;i<=n;i++)w[i].w=randInt()%n,w[i].id=i;
        for(int i=1;i<=n;i++)d[i]=randInt()%(maxd-mind+1)+mind;
    }
    void getOperation(int lastans,int &opt,int &x)
    {
        if((0ll+randInt()+lastans)%mfq)opt=1;
        else opt=2;
        x=(0ll+randInt()+lastans)%n;
    }
    int sum,used[N],wc,ch[N][2],s[N],tot;
    int max(int x,int y){return x>y?x:y;}
    void dfs(int now)
    {
        if(used[now]) return;
        used[now]=1;
        ++sum;
        dfs(ch[now][0]),dfs(ch[now][1]);
    }
    void init()
    {
        for(int i=1;i<=n;i++)
        {
            while(tot&&s[tot]+d[s[tot]]<i) --tot;
            if(tot) ch[i][0]=s[tot];
            s[++tot]=i;
        }
        tot=0;
        for(int i=n;i;i--)
        {
            while(tot&&s[tot]-d[s[tot]]>i) --tot;
            if(tot) ch[i][1]=s[tot];
            s[++tot]=i;
        }
        std::sort(w+1,w+1+n);used[0]=1;
        for(int i=1;i<=n;i++)
        {
            if(w[i].id==c) {wc=w[i].w;continue;}
            dfs(w[i].id);
            ans[0][w[i].w]=sum;
        }
        memset(used,0,sizeof(used));
        used[0]=1,sum=0,dfs(c);
        for(int i=1;i<=n;i++)
        {
            dfs(w[i].id);
            ans[1][w[i].w]=sum;
        }
        for(int i=N-10;~i;i--) ans[0][i]=max(ans[0][i],ans[0][i+1]),ans[1][i]=max(ans[1][i],ans[1][i+1]);
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&c);
        scanf("%llu%d%d%d%d",&seed,&mind,&maxd,&mfq,&k);
        generate();
        for (int p,t,i=1;i<=k;i++) scanf("%d%d",&p,&t),d[p]=t;
        init();
        int lastans=0,finalans=0;
        for(int ___,opt,x,i=1;i<=m;i++)
        {
            getOperation(lastans,opt,x);
            if(opt==1)
            {
                if(wc>=x) ___=ans[1][x];
                else ___=ans[0][x];
                finalans=(finalans*233ll+___)%998244353;
                lastans=___;
            }
            else
                wc=x;
        }
        printf("%d
    ",finalans);
        return 0;
    }
    

    2018.11.7

  • 相关阅读:
    一、【注解】Spring注解@ComponentScan
    一致性Hash算法
    垃圾回收器搭配和调优
    JVM的逃逸分析
    简单理解垃圾回收
    类加载机制和双亲委派模型
    VMWare15下安装CentOS7
    HBase协处理器(1)
    依赖注入的三种方式
    Javascript-设计模式_装饰者模式
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9925966.html
Copyright © 2011-2022 走看看