• 2018 Multi-University Training Contest 1


    Maximum Multiple

    题意:

      给出n,要求找到3个正整数x,y,z,使得x|n,y|n,z|n,x+y+z=n,并且x*y*z的值最大。

    分析:

      设x=n/r,y=n/s,z=n/t,且r<=s<=t,则1/r+1/s+1/t=1      =>     3*1/r >=1,即r<=3。

      当r=2时,1/s+1/t=1/2    =>    2*1/s >=1/2,即s<=4,在根据s的取值确定t值。

      同理,可以解出答案大概为(n/3,n/3,1/3),(n/4,n/4,n/2),(n/2,n/3,n/6)。

      再看如果一个数能被2,3,6整除,那么它也可以被2,4,4整除,但是2*3*6>2*4*4,所以排除第三种情况。

    代码:

    #include <map>
    #include <queue>
    #include <vector>
    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    #define ull unsigned long long
    #define cls(x) memset(x,0,sizeof(x))
    #define clslow(x) memset(x,-1,sizeof(x))
    
    const int maxn=1e6+100;
    const int inf=0x3f3f3f3f;
    
    int n,T;
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        scanf("%d",&T);
        while(T--)
        {
            ll ans=1;
            scanf("%d",&n);
            if(n%3==0){
                ans=ans*(n/3)*(n/3)*(n/3);
            }
            else if(n%4==0){
                ans=ans*(n/4)*(n/4)*(n/2);
            }
            else{
                ans=-1;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code

    Balanced Sequence

    题意:

      把n个字符串任意拼接后,求最长平衡子串(不一定连续)。定义平衡子串为连续的一段子串且左右括号匹配。是不是看上去有点绕,对于第一个样例)()(()(,它的平衡子串为【1,2】和【4,5】,这里【1,2】或者【4,5】的子串是连续的并且左右括号匹配,但是对于这两个子串来说他们是不连续的。

    分析:

      既然可以不用连续,我们首先把匹配的子串分离出来,然后每个字符串就变成了a个右括号和b个左括号,然后问题就变成了该怎么拼接这n个字符串。具体排序规则在代码中有解释。

      参考资料:大佬博客

    #include <set>
    #include <queue>
    #include <vector>
    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    #define ll long long
    #define ull unsigned long long
    #define cls(x) memset(x,0,sizeof(x))
    #define clslow(x) memset(x,-1,sizeof(x))
    #define FI(n) FastIO::read(n)
    
    const int maxn=1e5+100;
    
    int n,q,T;
    
    char s[maxn];
    
    struct Node {
        ll l,r;
        bool operator < (const Node& rhs) const {
            //左少右多 vs 左多右少
            if(l<=r&&rhs.l>=rhs.r)  return false;
            //左多右少 vs 左少右多
            else if(l>=r&&rhs.l<=rhs.r) return true;
            //左多右少 vs 左多右少
            else if(l>=r&&rhs.l>=rhs.r) return r<rhs.r;
            //左少右多 vs 左少右多
            else if(l<=r&&rhs.l<=rhs.r) return l>rhs.l;
        }
    };
    Node node[maxn];
    
    void solve(ll& l,ll& r,ll& ans)
    {
        int len=strlen(s);
        for(int i=0;i<len;i++){
            if(s[i]=='(')   l++;
            else{
                if(l>0) l--,ans+=2;
                else    r++;
            }
        }
    }
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        scanf("%d",&T);
        while(T--)
        {
            ll ans=0;
            scanf("%d%d",&n);
            for(int i=1;i<=n;i++){
                scanf("%s",s);
                node[i].l=node[i].r=0;
                solve(node[i].l,node[i].r,ans);
            }
    
            sort(node+1,node+n+1);
            ll L=node[1].l,R=node[1].r;
            for(int i=2;i<=n;i++){
                if(L<=node[i].r){
                    ans+=L*2;
                    R+=node[i].r-L;
                    L=node[i].l;
                }
                else{
                    ans+=node[i].r*2;
                    L+=node[i].l-node[i].r;
                }
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code

    Triangle Partition

    题意:

      给出3*n个点,3个点组成一个三角形,并且所有三角形之间不相交。

    分析:

      按照坐标的x,y坐标排序后,从小到大挑选。官方题解是用了凸包,赤裸裸的水过~~~

    代码:

    #include <map>
    #include <queue>
    #include <vector>
    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    #define ull unsigned long long
    #define cls(x) memset(x,0,sizeof(x))
    #define clslow(x) memset(x,-1,sizeof(x))
    
    const int maxn=1e6+100;
    const int inf=0x3f3f3f3f;
    
    int n,T;
    
    struct Point {
        int x,y,id;
    };
    Point p[maxn];
    
    bool cmp(Point a,Point b)
    {
        if(a.x==b.x)    return a.y<b.y;
        return a.x<b.x;
    }
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            n=n*3;
            for(int i=1;i<=n;i++){
                p[i].id=i;
                scanf("%d%d",&p[i].x,&p[i].y);
            }
    
            sort(p+1,p+n+1,cmp);
            for(int i=1;i<=n;i+=3){
                for(int j=i;j<i+3;j++){
                    if(j>i) printf(" ");
                    printf("%d",p[j].id);
                }
                printf("
    ");
            }
        }
        return 0;
    }
    View Code

    Distinct Values

    题意:

      给出数组的长度n,m个子区间,怎样赋值使得给出子区间内的每一个值都不相同,同时使得总数组的字典序最小。

    分析:

      先把子区间个按照l,r从小到大排个序。如果相邻的区间不想交,直接从1开始赋值就好了,如果有交集,用vis数组标记交集部分用了什么数,给该区间赋值时跳过这些值就好了。

    代码:

    #include <set>
    #include <queue>
    #include <vector>
    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    #define ll long long
    #define ull unsigned long long
    #define cls(x) memset(x,0,sizeof(x))
    #define clslow(x) memset(x,-1,sizeof(x))
    #define FI(n) FastIO::read(n)
    
    const int maxn=1e5+100;
    
    int n,q,T;
    int L,R,lastL,lastR;
    
    int a[maxn],vis[maxn];
    
    struct Line {
        int l,r;
        bool operator < (const Line& rhs) const {
            if(l==rhs.l)    return r<rhs.r;
            return l<rhs.l;
        }
    };
    Line l[maxn];
    
    //flag:true 从1开始赋值 false:全部赋值为1
    void fillArray(int l,int r,int start,bool flag)
    {
        int val=start;
        for(int i=l;i<=r;i++){
            a[i]=val;
            if(flag)    val++;
        }
    }
    
    //处理交集部分
    void solve(int pos,int pre)
    {
        L=l[pos].l,R=l[pos].r;
        lastL=l[pre].l,lastR=l[pre].r;
    
        //交集部分的数字标记为要赋值区间的id
        for(int i=L;i<=lastR;i++){
            vis[a[i]]=pos;
        }
        int c=1;
        for(int i=lastR+1;i<=R;i++){
            //如果c在该区间被赋值过,跳过
            while(vis[c]==pos)  c++;
            a[i]=c;
            vis[c]=pos;
        }
    }
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        scanf("%d",&T);
        while(T--)
        {
            cls(vis);
            scanf("%d%d",&n,&q);
            for(int i=1;i<=q;i++){
                scanf("%d%d",&l[i].l,&l[i].r);
            }
            sort(l+1,l+q+1);
    
            int la=1;
            for(int i=1;i<=q;i++){
                L=l[i].l,R=l[i].r;
                lastL=l[la].l,lastR=l[la].r;
                //第一个子区间
                if(i==1){
                    fillArray(1,L-1,1,false);
                    fillArray(L,R,1,true);
                    continue;
                }
    
                //如果有交集
                if(L<=l[la].r){
                    //如果该区间包括在上一个区间内,跳过,因为一定是不同的
                    if(R<=l[la].r)    continue;
                    solve(i,la);
                }
                else{
                    fillArray(lastR+1,L-1,1,false);
                    fillArray(L,R,1,true);
                }
                la=i;
            }
            //处理后面剩余的部分
            fillArray(l[la].r+1,n,1,false);
    
            for(int i=1;i<=n;i++){
                if(i>1) printf(" ");
                printf("%d",a[i]);
            }
            printf("
    ");
        }
        return 0;
    }
    View Code

    Chiaki Sequence Revisited

    题意:

      给出一个数组的通项公式,求前n项的和。

    分析:

      打表找规律题?大佬说什么就是什么吧……数组的值是没有什么规律的,但是数组的值出现的次数有规律,wtf。

    好像看上去没什么规律,但是如果把第一项变为1,规律就出来了。

    前2^n项的值为前2^(i-1)的值复制后,再把第2^i项+1,什么意思?好像还是有点难懂。

    比如1,2 -> 1,3     1,2,1,3 -> 1,2,1,4    1,2,1,3,1,2,1,4 -> 1,2,1,3,1,2,1,5

    然后通过打表找规律可以知道出现次数为i的数的首项为2^(i-1),公差为2^i(万恶的打表啊),所以我们就可以通过二分先求出第n个数的值,再通过等差数列求和得到前n个数的和。参考的大佬的博客,对代码加上了自己的理解。

    参考资料:大佬博客

    代码:

    #include <set>
    #include <queue>
    #include <vector>
    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    #define ll long long
    #define ull unsigned long long
    #define cls(x) memset(x,0,sizeof(x))
    #define clslow(x) memset(x,-1,sizeof(x))
    
    const int mod=1e9+7;
    const int maxn=1e5+100;
    
    ll n,T,inv;
    
    //num:2^i区间共有多少数
    //all:2^i区间所有数出现次数之和
    ll num[maxn],all[maxn];
    
    void init()
    {
        //2的逆元
        inv=(mod+1)>>1;
    
        num[0]=1;all[0]=1;
        for(int i=1;i<=62;i++){
            num[i]=num[i-1]*2;
            all[i]=all[i-1]*2+1;
        }
    }
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        init();
        scanf("%lld",&T);
        while(T--)
        {
            ll ans=0;
            scanf("%lld",&n);
    
            //计算出第n个数的值pos
            ll m=n,pos=0;
            for(int i=62;i>=0;i--){
                if(m-all[i]>=0){
                    m-=all[i];
                    pos+=(1ll<<i);
                }
            }
            //如果pos出现的次数少于总共的次数
            pos+=(m>0);
            //可能pos的值没有全部出现,所以放在后面计算
            //cnt:总共计算了多少数的和
            ll now=pos-1,cnt=0;
            //出现次数为i的数,首项为2^(i-1),公差为2^i
            for(int i=1;i<=62;i++){
                if(num[i-1]>now)  break;
                //首项 项数 末项
                ll s=num[i-1],step=(now-s)/num[i]+1,e=s+(step-1)*num[i];
                //出现次数为i的数的和
                ll sum=(s+e)%mod*(step%mod)%mod*inv%mod;
                ans=(ans+sum*i%mod)%mod;
                //出现次数为i的数有step项
                cnt+=step*i;
            }
            //把出现次数为pos的数的和加入答案
            //因为把第一项2变为了1,所以答案需要+1,计算的项数之和需要减一
            ans=(ans+1+(n-cnt-1)%mod*(pos%mod))%mod;
    
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code

    RMQ Similar Sequence

    题意:

      定义RMQ(A,l,r)为A数组在【l,r】区间内最大值的最小下标,如果两个数组任意子区间的RMQ都相同,那么定义这两个数组是同构的。现在给出A数组,B数组为在【0,1】之间的任意实数,且符合均匀分布。如果A,B同构,那么B的权值为,否则为0,输出B的权值的期望是多少。

    分析:

      以下截取自大佬博客:

      

      对于这段话我说下我的理解吧,首先如果RMQ Similar,则A和B笛卡尔树同构,这个需要看一下笛卡尔树的性质。

      

      因为按照中序遍历可以得到原数组,所以我们可以认为一个子树就是一个子区间。并且,加上第二条性质,如果同构,那么子区间一定是RMQ Similar的,实在不行就手动模拟一下,很容易看出来的。

      对于任一B排列与A同构的概率,先考虑一个数时,无论怎么放,都是同构的。在加上第二个数,这时候就要考虑这个子区间对应的子树,我可以放在这个子树上对应的任意位置,所以这个数放对的概率就是1/sz……对每一个数都求一遍概率,所以整体同构的概率就是每一个数对应的概率相乘。

      均匀分布的期望为1/2*(b-a),为1/2,所以nX的期望为nE(x),为n/2.

      所以整体期望就为,因为有取模,所以求下逆元。

      参考资料:笛卡尔树  大佬题解

    #include <stack>
    #include <queue>
    #include <vector>
    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    #define ll long long
    #define ull unsigned long long
    #define cls(x) memset(x,0,sizeof(x))
    #define clslow(x) memset(x,-1,sizeof(x))
    
    const int mod=1e9+7;
    const int maxn=1e6+100;
    const int inf=0x3f3f3f3f;
    
    ll inv[maxn];
    stack<int>st;
    
    struct Tree {
        int sz,val;
        int l,r,fa;
    };
    Tree t[maxn];
    
    void init(int n)
    {
        while(!st.empty())  st.pop();
        for(int i=0;i<=n;i++){
            t[i].l=t[i].r=t[i].fa=t[i].val=t[i].sz=0;
        }
        t[0].val=inf;
        st.push(0);
    }
    
    //建立一颗笛卡尔树
    void build(int n)
    {
        for(int i=1;i<=n;i++){
            while(!st.empty()&&t[st.top()].val<t[i].val)    st.pop();
            int fa=st.top();
            t[i].fa=fa;
            t[i].l=t[fa].r;
            t[t[fa].r].fa=i;
            t[fa].r=i;
            st.push(i);
        }
    }
    
    //求每一个子树的大小
    void dfs(int u)
    {
        if(u==0)    return;
        t[u].sz=1;
        dfs(t[u].l);
        dfs(t[u].r);
        t[u].sz+=t[t[u].l].sz+t[t[u].r].sz;
    }
    
    //求1~maxn所有数的逆元
    void  getInv()
    {
        inv[1]=1;
        for(int i=2;i<maxn;i++){
            inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        }
    }
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        int n,T;
        getInv();
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d",&n);
            init(n);
            for(int i=1;i<=n;i++){
                scanf("%d",&t[i].val);
            }
    
            build(n);
            dfs(t[0].r);
            ll ans=n*inv[2]%mod;
            for(int i=1;i<=n;i++){
                ans=ans*inv[t[i].sz]%mod;
            }
    
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code

    Time Zone

    题意:

      给出北京时间,输出所给时区的相对应的时间。

    分析:

      首先要知道怎么不同时区时间怎么转换,因为给定了UTC+X,所以该时区的时钟就是h+(X-8),分钟也对应这么处理一下,注意各种细节的处理就好了。

    代码:

    #include <map>
    #include <queue>
    #include <vector>
    #include <math.h>
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    #define ull unsigned long long
    #define cls(x) memset(x,0,sizeof(x))
    #define clslow(x) memset(x,-1,sizeof(x))
    
    const int maxn=1e6+100;
    const int inf=0x3f3f3f3f;
    
    int T,a,b,x,y;
    bool negative,flag;
    
    char s[10];
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        scanf("%d",&T);
        while(T--)
        {
            x=y=0;
            flag=false;
            scanf("%d%d%s",&a,&b,s);
            int len=strlen(s);
    
            if(s[3]=='-')   negative=true;
            else            negative=false;
            for(int i=4;i<len;i++){
                if(s[i]=='.'){
                    flag=true;
                    continue;
                }
                if(flag)    y=s[i]-'0';
                else        x=x*10+(s[i]-'0');
            }
            if(negative)    x=-x,y=-y;
            if(flag){
                b=b+y*60/10;
                if(b>=60)   a++,b=b%60;
                if(b<0)     a--,b=(b+60)%60;
            }
            a=((a+x-8+24-1)%24+1)%24;
            printf("%02d:%02d
    ",a,b);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    Vue 多层组件传值(最外层组件>中间组件>最内部组件)
    Vue 父子组件传值 (Vue-cli4)
    CSS HTML 基本知识 盒子模型 Flex等
    Vue---基本知识
    springboot ---邮件和定时任务 和异步
    js中.bind()和.call()用法讲解
    为页内的tab添加的iframe添加加载动画过渡效果
    JMETER学习宝典
    新篇章,新起点
    计划拟定(现阶段)
  • 原文地址:https://www.cnblogs.com/shutdown113/p/9361672.html
Copyright © 2020-2023  润新知