zoukankan      html  css  js  c++  java
  • 杭州集训第四天

    今天是本蒟蒻在杭州集训(被虐)的第四天,由于我太弱了,出题人不屑于浪费时间出毒瘤题来坑我;而是从洛谷上

    挪了些原题上来;但由于我太弱了,还是没做出来;

    T1 . 坐等 memset0
    树链剖分是个喜欢逛讨论区的女孩子。
    树链剖分看到有若干个小学生发的帖子,因为一些原因,这些帖子形
    成了一条链。
    其中第 i(1<=i< n)个帖子和第 i+1 个帖子用双向边相连
    树链剖分举报了这些帖子,然后作为管理员的 memset0,决定对帖子
    进行删除。
    其中第 i 个帖子有一个小学生值 a.

    但是, 因为 memset0 太强了, memset0 不屑于对小学生值在[l,r] 区
    间内的帖子进行删除,只和谐掉其他帖子。(即,只保留小学生值[l,r
    的帖子)
    现在,树链剖分想到一个问题,就是在这个保留下来的帖子里,联通
    块的个数是多少。
    问题还没完
    我们把只保留小学生值[l,r]的帖子使得这些帖子的联通块的个数记

    输入:
    第一行一个正整数 n
    第二行 n 个正整数 a[i]
    输出
    一行一个正整数,表示答案

    样例输入
    4
    2 1 1 3
    样例输出
    11
    对于 40%的数据 n <= 100
    对于 60%数据 n <= 1000
    对于 100%数据 n <= 100000,1<=a[i]<=n 且为正整数

    这题在洛谷原题是CF1151E有兴趣的可以去做一下;

    然后,我讲下这题怎么做,首先如果打暴力的话是只有60分;

    接下来,我讲讲从隔壁大佬那学来的正解:

    考虑每个点对答案的贡献,我们认为一个连通块里让编号最大的点

    产生贡献,对于点 i,要产生贡献就一定要这个点存在,而点 i+1不

    存在而点n只用包含这个点就行了

    具体是这样的:

    当 a i>a i+1 贡献=(a i-a i+1)*(n-a i+1)

    当 a i<a i+1 贡献=(a i+1-a i)*a i;

    具体代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    long long n;
    long long ans;
    long long a[1000005];
    int main()
    {
        scanf("%lld",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
        }
        for(int i=1;i<n;i++)
        {
            int x=a[i],y=a[i+1];
            if(x>y)
            {
                ans=(long long)ans+(x-y)*(n-x+1);
            }
            else
            {
                ans=ans+(long long)(y-x)*x;
            }
        }
        ans+=(long long)a[n]*(n-a[n]+1);
        cout<<ans;
        return 0;
    }
    T2.吊打集训队:
    树链剖分是一个喜欢膜巨佬的女孩子 一天树链剖分正在看 WXW 吊打集训队。
    WXW 在数轴的 0 位置,其中数轴上的整点(包括 0)上有一个队爷。
    如果当前 wxw 在位置 k,则 wxw 可以跳到 k+a 或 k-b 位置。wxw 只要 在一个位置上,就可以吊打该位置的集训但是 WXW 觉得这样很无聊,
    于是他搞了一个函数 f(X)表示 wxw 只能 在区间[0,x]中吊打集训队(wxw 只能在区间[0,x]里面,无法出来),从 开始最多能吊打的队爷。
    注:wxw 可以多次经过同一个位置,但同一个位置只算吊打一个队爷 wxw
    想要求∑  m i=0 f(i)
    输入: 3 个正整数 m,a,b
    输出 Wxw 想要求的结果
    样例输入 7 5 3
    样例输出 19
    样例输入 2
    6 4 5
    样例输出 2
    10
    数据范围: 对于 20%的数据 m,a,b <= 100
    对于 40%数据 m,a,b <= 1000
    对于 60%的数据 m,a,b <= 100000
    对于 100%的数据 m <= 1000,000,000 a,b <= 100,00

    这题也是洛谷原题,题号是:CF1146D
    这题写暴力的话只有40分;

    下面我来说一下正解:
    先暴力求出前2*max(a,b)的方案数

    在通过打表找到规律:

    当 i>2*max(a,b)时;

    f(i)=i/gcd(a,b)+1;

    (其实i > a+b就有规律了)

    关于这个规律的证明

    首先用exgcd可以说明 不是gcd(a,b)倍数的点无法到达

    ax-by = gcd(a,b)*k

    现在可以用exgcd求出x,y

    然后我们就是要走xayb

    假如a < b的话

    那么就wxw就可以走若干个+a,直到大于b时候往回跳一次,然后继续+a

    a>b同理

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    long long n;
    long long ans;
    long long a,b,i;
    long long gcd(long long a,long long b){return b?gcd(b,a%b):a;}
    int main()
    {
        scanf("%lld%lld%lld",&n,&a,&b);
        ans+=a;
        int d=gcd(a,b);
        if(n<a){
            printf("%d",n+1);
            return 0;
        }
        long long tot=1,w=0;
        for(i=a;i<=2*max(a,b);i++)
        {
            while(w+a<=i)
            {
                tot+=(i-w)/a;
                w+=(i-w)/a*a;
                tot+=w/b;
                w-=w/b*b;
                if(!w) break;
            }if(!w) break;
            ans+=tot; if(i==n) break;
        }
        if(!w)
        {
            long long d=gcd(a,b); long long g=i/d+1;
            long long k=n/d+1; 
                long long sum=(long long)(n/d)*d;
                ans+=(long long)(g+k)*(sum-i+d)/2;
                long long total=0;
                for(long long j=n+1;j<sum+d;j++)
                {
                    total+=(long long)j/d+1;
                }
                ans-=total;
        }
        cout<<ans;
        return 0;
    }
    T3:Ynoi:
    树链剖分是个爱出题的女孩子。
    ccz 出了一道数据结构题,结果太毒瘤了,树链剖分不会做
    czz 也出了一道数据结构题,结果还是太毒瘤了,
    树链剖分还是不会 做 树链剖分也出了一道数据结构题,结果太水了,被大家秒了
    后来,ccz,czz,树链剖分决定合出一道题 然后 ccz 觉得树链剖分太菜了,不屑于一起参与出题,于是去找 lxl 一
    起出 Ynoi 了。
    czz 也觉得树链剖分太菜了,但还是随手说了两个询问操作。
    树链剖分:“那我干啥呢?”
    czz:“你这么菜,出出来的题也是被大家秒掉。”
    树链剖分:“好吧,那我造数据吧。
    ” 结果由于树链剖分太菜了,她造的数据全是随机的。
    现在有一个 1~n 的排列 a,
    现在有以下操作
    1 l r p 求 a[i]模 p 后的值在模 p 意义下的逆元的最大值(l <= i <= r)(保 证 p 为质数,p <= 100000)(如果 a[i]是 p 的倍数,则认为逆元是 0) 2 l r k 对于序列的第 i 个数,表示 i 号点,从 a[i]向 i 连一条有向边, 求从点 i(l<=i<=r)开始出发恰好经过 k 条边,到的点编号的最大值(可以 多次经过同一个点)。如果没有这样的点,输出-1;
    保证数据随机
    输入:
    第一行 两个正整数 n,m
    表示序列长度和询问次数
    第 2~m+1 行,每行若干个整数,表示一个操作
    输出: m 行,输出一个数,询问的答案

     样例输入 5 2

    4 3 1 5 2

    1 1 4 3

    2 2 5 3

    样例输出

    2

    4

    数据范围 20%数据 n,m <= 100
    50%数据 n,m <= 1000
    100%数据 n,m <= 100000 ,
    所有输入的数均为正整数且小于等于 n。 1 <= l <= r <= n,且随机生成。
    这题是出题人随手出的一道题,然后我就连暴力都打不来了;
    经过隔壁大佬的一下午教导;我终于学会了正解;
    其实也蛮简单的;

    第一个询问,从p-10枚举答案,求逆,判断是否在区间[l,r]内,如果在就输出并退出

    第二个询问一样,从n~1枚举答案,倍增(或用成环,因为是个排列)判断k步前是哪个点。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m;
    struct adw{
        int ma,zhi;
    }a[110005];
    int b,l,r,p,k,z;
    int f[110000];
    int fa[110000][21];
    int w[21];
    int q[110000];
    int rd(){
        int s=0,ff=1;
        char ww=getchar();
        while(ww<'0'||ww>'9'){
            if(ww=='-') ff=-1;
            ww=getchar();
        }
        while(ww>='0'&&ww<='9'){
            s=s*10+(ww-'0');
            ww=getchar();
        }
        return s*ff;
    }
    int kuai(int a,int b,int p)
    {
        int sum=1;
        while(b)
        {
            if(b&1) sum=(long long)sum*a%p;
            a=(long long)a*a%p;
            b=(b>>1);
        }
        return sum;
    }
    void work()
    {
        bool h=0;
        for(int i=p-1;i>=0;i--)
        {
            int c=kuai(i,p-2,p);
            for(int j=c;j<=n;j+=p)
            {
                if(a[j].ma>=l&&a[j].ma<=r)
                {
                    printf("%d
    ",i);
                    h=1;
                    break;
                }
            }
            if(h==1)
            break;
        }
    }
    void dfs(int x,int d)
    {
        if(f[x]){q[x]=d; return;}
        f[x]=1; dfs(a[x].zhi,d+1);
        q[x]=q[a[x].zhi];
    }
    int QWQ(int x,int s)
    {
        for(int i=z;i>=0;i--)
        {
            if(s>=w[i])
            {
                s-=w[i];
                x=fa[x][i];
            }
        }
        return x;
    } 
    void QwQ()
    {
        for(int i=n;i>=1;i--)
        {
            int ch=QWQ(i,k%q[i]);
            if(ch>=l&&ch<=r)
            {
                printf("%d
    ",i);
                break;
            }
        }
    }
    int main()
    {
        n=rd();
        m=rd();
        w[0]=1;
        z=log2(n);
        for(int i=1;i<=n;i++)
        {
            a[i].zhi=rd();
            a[a[i].zhi].ma=i;
            fa[i][0]=a[i].zhi;
        }
        for(int i=1;i<=z;i++)
        w[i]=(w[i-1]<<1);
        for(int i=1;i<=n;i++)
        if(f[i]==0) dfs(i,0);
        for(int i=1;i<=z;i++)
            for(int j=1;j<=n;j++)
                fa[j][i]=fa[fa[j][i-1]][i-1];
        for(int i=1;i<=m;i++)
        {
            b=rd();
            if(b==1)
            {
                l=rd();
                r=rd();
                p=rd();
                work();
            }
            if(b==2)
            {
                l=rd();
                r=rd();
                k=rd();
                QwQ();
            }
        }
        return 0;
    }

    对了,记得一定要用read函数读入,不然会被卡,不要问我我为什么会知道; 

    以上就是我被虐的全部过程;

    来自一个蒟蒻;

     
  • 相关阅读:
    【转】Linux进程间通信——管道及有名管道
    【转】linux进程间通信——信号(上)
    【转】Linux进程间通信—— 共享内存(下)
    【转】linux进程间通信——信号(下)
    python 文件
    文本分类入门(转)
    五大主流数据库模型 (转)
    使用libSVM
    Python编程之前戏——Python环境搭建与编译器的选择 Java(转)
    最棒的10款MySQL GUI工具 (转)
  • 原文地址:https://www.cnblogs.com/hong13/p/11166431.html
Copyright © 2011-2022 走看看