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\n",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\n",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("\n");
        }
    }
    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("\n");
    }
    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\n",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\n",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\n",a,b);
    }
    return 0;
}
View Code

 

posted on 2018-07-25 10:37  我过了样例耶  阅读(138)  评论(0编辑  收藏  举报

导航