• 算法马拉松13 A-E解题报告


    A题意(取余最长路):

      佳佳有一个n*m的带权矩阵,她想从(1,1)出发走到(n,m)且只能往右往下移动,她能得到的娱乐值为所经过的位置的权的总和。

    有一天,她被下了恶毒的诅咒,这个诅咒的作用是将她的娱乐值变为对p取模后的值,这让佳佳十分的不开心,因为她无法找到一条能使她得到最大娱乐值的路径了!

    她发现这个问题实在是太困难了,既然这样,那就只在3*n的矩阵内进行游戏吧!

    现在的问题是,在一个3*n的带权矩阵中,从(1,1)走到(3,n),只能往右往下移动,问在模p意义下的移动过程中的权总和最大是多少。

    n(1<=n<=100000),p(1<=p<=1000000000)。

      最简单的思路就是 搞一搞前缀和 枚举两个转折点 那么复杂度是n^2。BOOM!

      设三段的前缀和和 分别为s1,s2,s3  设转折点分别为(2,x1) (2,x2)

    再仔细想一想p-1肯定是我们能得到的最大值,那么我们可以优化到枚举一个转折点(第二个转折点),前两段的结果丢在set里;

    二分就OK了。

    但是为了不影响二分的结果,做了一点改动(set不能丢入1,1->2,x1->2,x2)。应丢入1,1->2,x1->2,n 的和

    前缀和表示为 s2[n]+s1[x1]-s2[x1-1];

    二分的值变为((p-1)-(s3[n]-s3[x2-1])+((s2[n]-s2[x2]))+p)%p,每次更新答案就OK了

    再观察一下发现插入和查询的时候都加了s2[n]

    所以我们把s2[n]去掉=。=

    然后s3[]用的一直是x2->n的和  这里应该用个后缀和的

    写的时候手残读入错误,导致以为思路不对 --- 浪费了很多时间  (被自己气笑

    PS:啊现在的我们是多么幸福,现成的STL啦啦啦;

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <set>
    using namespace std;
    const int maxn = 1e5+10;
    typedef long long ll;
    inline void r(ll&num){
        num=0;ll f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
        num*=f;
    }
    inline void r(int &num){
        num=0;int f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
        num*=f;
    }
    ll s1[maxn],s2[maxn],s3[maxn];
    int main()
    {
        int n;
        ll p;
        r(n),r(p);
        for(int i=1;i<=n;i++)
        {
            r(s1[i]);
            s1[i]=(s1[i]+s1[i-1])%p;
        }
         for(int i=1;i<=n;i++)
        {
            r(s2[i]);
            s2[i]=(s2[i]+s2[i-1])%p;
        }
         for(int i=1;i<=n;i++)
        {
            r(s3[i]);
            s3[i]=(s3[i]+s3[i-1])%p;
        }
        set<ll>s;
        ll ans = -1;
        set<ll>::iterator it;
        ll sum;
        ll cnt;
        for(int i=1;i<=n;i++)
        {
            s.insert(((s1[i]-s2[i-1])%p+p)%p);
            sum = (s3[n]-s3[i-1]+s2[i])%p;
            cnt = ((p-sum)%p+p)%p;
            it = s.lower_bound(cnt);
            if(it!=s.begin()) it--;
            ans = max(ans,((((*it)+sum)+p)%p));
        }
        cout<<ans<<endl;
        return 0;
    }
    有漏洞

     这里有一个小技巧  我们如果想找出小于等于X的最大值 我们只需lower_boundX+1  得到大于等于X+1的区间[it,end)  那么 [begin,it) 就是小于等于X的区间;

    if it == begin 不存我们要的值

    else *(--it)就是我们要的值辣

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <set>
    using namespace std;
    const int maxn = 1e5+10;
    typedef long long ll;
    inline void r(ll&num){
        num=0;ll f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
        num*=f;
    }
    inline void r(int &num){
        num=0;int f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
        num*=f;
    }
    ll s1[maxn],s2[maxn],s3[maxn];
    int main()
    {
        int n;
        ll p;
        r(n),r(p);
        for(int i=1;i<=n;i++)
        {
            r(s1[i]);
            s1[i]=(s1[i]+s1[i-1])%p;
        }
         for(int i=1;i<=n;i++)
        {
            r(s2[i]);
            s2[i]=(s2[i]+s2[i-1])%p;
        }
         for(int i=1;i<=n;i++)
        {
            r(s3[i]);
            s3[i]=(s3[i]+s3[i-1])%p;
        }
        set<ll>s;
        ll ans = -1;
        set<ll>::iterator it;
        ll sum;
        ll cnt;
        for(int i=1;i<=n;i++)
        {
            s.insert(((s1[i]-s2[i-1])%p+p)%p);
            sum = (s3[n]-s3[i-1]+s2[i])%p;
            cnt = ((p-sum)%p+p)%p;
            it = s.lower_bound(cnt);
            if(it!=s.begin()) it--;
            else    it = --s.end();
            ans = max(ans,((((*it)+sum)+p)%p));
        }
        cout<<ans<<endl;
        return 0;
    }
    AC代码

     C题意(比大小):

       有两个数列A和B 已知A_0,a,b,N A_n=A_(n-1)*a+b (n>=1) B数列满足 B_n=2*B_(n/2) + 1 (n为偶数) B_n=2*B_((n-1)/2) + (n+1)/2 (n为奇数)
    现在问B数列的第A_N项和第(A_N)+1项的关系
    T组数据 A_0,a,b,N<=1e15

    T<=100
     
    N 1e15  没规律铁定做不了
    B_0 = -1;
    然后把B序列暴力出来前40项  发现规律 除了0-3 不符合规律外 后面连续的四个都符合规律
    这时候就需要考虑A序列了 如果发现A_N小于4 那就需要特判 (题意说的也不是太清如果 A_0<=3 ,  a = 0,b = 0, N = 1e15 这个数据肯定T
    然后暴力N项 发现还有规律
    N<=3 特判
    N>=4  走N次 和 N%4+4结论一样
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <set>
    using namespace std;
    typedef long long ll;
    inline void r(ll&num){
        num=0;ll f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
        num*=f;
    }
    inline void r(int &num){
        num=0;int f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
        num*=f;
    }
    ll a,b,N;
    ll A0,AN,A;
    bool flag = false;
    ll B[40];
    void check()
    {
        if(flag)
        {
            A+=4;
        }
        if(B[A]==B[A+1])
        {
            puts("=");
        }
        else{
            if(B[A]<B[A+1])
            {
                puts("<");
            }
            else{
                puts(">");
            }
        }
    }
    int main()
    {
        int T;
        r(T);
        B[0] = -1;
        //cout<<"-1"<<endl;
        for(int i=1;i<40;i++)
        {
            B[i] = B[i/2]*2+1;
            if(i&1)
            {
                B[i]+=i/2;
            }
            //cout<<B[i]<<endl;
        }
        while(T--)
        {
            flag = false;
            r(A0),r(a),r(b),r(N);
            A = A0;
            for(ll i=1;i<=N;i++)
            {
                
                if(A>4)
                {
                    flag = true;
                    break;
                }
                A = A*a+b;
                //A%=4;
            }
            A = A0%4;
            N = N > 3 ? N % 4 + 4 : N;
            for(int i=1;i<=N;i++)
            {
                A = A*a+b;
                A%=4;
            }    
            check();
        }
        return 0;
    }
    AC代码
     
  • 相关阅读:
    微服务实战(二):使用API Gateway
    微服务实战(一):微服务架构的优势与不足
    在WIN7、WIN10操作系统用WebDAV映射网络驱动器需要的操作
    docker开机启动和docker-compose开机启动执行相应的各个docker容器
    /etc/rc.d/init.d自启动程序说明
    C# 通过反射实现对象映射:将2个属性相近的对象相互转换
    添加windows右键菜单:使用exe应用程序打开文件/文件夹
    .NET5 MVC Program.cs 笔记
    前端 JS 正则表达式积累
    VS Code 快捷键
  • 原文地址:https://www.cnblogs.com/Geek-xiyang/p/5785437.html
Copyright © 2020-2023  润新知