2018 Multi-University Training Contest 1

Posted heyuhhh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2018 Multi-University Training Contest 1相关的知识,希望对你有一定的参考价值。

传送门

A - Maximum Multiple

推一下式子暴力判断即可,范围不会太大。


Code

#include<bits/stdc++.h>
typedef long long ll;
typedef double db;
using namespace std;
int a[15][3]
        2,3,6,
        2,4,4,
        2,6,3,
        3,2,6,
        3,3,3,
        3,6,2,
        4,2,4,
        4,4,2,
        6,2,3,
        6,3,2
;
int t,n;
int main()
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
        cin>>n;
        ll ans=-1;
        for(int i=0;i<10;i++)
            if(n%a[i][0]==0 && n%a[i][1]==0 && n%a[i][2]==0)
                ans = max(ans,(ll)(n/a[i][0])*(n/a[i][1])*(n/a[i][2]));
            
        
        cout<<ans<<'\\n';
    
    return 0;

B - Balanced Sequence

题意:
给出\\(n\\)个串,现在定义“好串”:

  • 空串;
  • \\(A,B\\)为“好串”,那么\\(AB\\)会“好串”;
  • \\(A\\)为“好串”,那么\\((A)\\)为“好串”。

现在可以对\\(n\\)个串的顺序任意排列之后拼接起来,问最长“好串”子序列的长度。

思路:

  • 对于每个串,先处理出合法括号序列记入答案,那么最后每个串都形如\\("))))((("\\)的形式,我们将其记为\\((L,R)\\),表示左括号和右括号分别有多少个。
  • 接下来考虑怎么安排顺序使得答案最优。
  • 考虑两个的情况,那么答案为\\(min(L_1,R_2)\\)\\(min(R_1,L_2)\\),这时我们按照\\(min(L_1,R_2)<min(R_1,L_2)\\)的顺序放置即可。
  • 为什么?
口胡一下自己的理解 对于两个而言,这样肯定最优;假设对于$k$个这样最优,考虑第$k+1$个加进来,此时放在最后面,根据排序规则有,我们假设从后面往前面匹配,假设最后两个多出$($则不用管,否则多出$)$可能会对前面产生贡献;若没有放在最后,而在中间某个位置,它多出来的一些$)$则会对更少的一段产生贡献,并且显然,此时多处来的$)$数目只可能更多,显然不优。
  • 注意一下当\\(min(L_1,R_2)=min(R_1,L_2)\\)时,我们要按照\\(L\\)从大到小排序,这可以让更多的\\(L\\)对后面产生贡献。

详见代码:


Code

#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
//#define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 5;

int n;
char s[N];
int sta[N], top;

struct Point
    int L, R;
    bool operator < (const Point &A)const 
        int p = min(R, A.L), q = min(L, A.R);
        if(p == q) return L > A.L;
        return p < q;
    
p[N];

void run() 
    cin >> n;
    int ans = 0;
    for(int i = 1; i <= n; i++) 
        cin >> s + 1;
        int len = strlen(s + 1);
        int l = 0, r = 0;
        top = 0;
        for(int j = 1; j <= len; j++) 
            if(top && s[sta[top]] == '(' && s[j] == ')') --top, ++ans;
            else sta[++top] = j;
        
        for(int j = 1; j <= top; j++) 
            if(s[sta[j]] == '(') ++l;
            else ++r;
        
        p[i] = l, r;
    
    sort(p + 1, p + n + 1);
    int l = 0, r = 0;
    for(int i = 1; i <= n; i++) 
        int now = min(l, p[i].R);
        ans += now;
        l = l - now + p[i].L;
    
    cout << ans * 2 << '\\n';


int main() 
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    int T; cin >> T;
    while(T--) run();
    return 0;

C - Triangle Partition

排序每次选左边的就行。


Code

#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
//#define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 3e3 + 5;

struct Point
    int x, y, id;
    bool operator < (const Point &A) const 
        return x < A.x;
    
p[N];
int n;

void run() 
    cin >> n;
    for(int i = 1; i <= 3 * n; i++) 
        cin >> p[i].x >> p[i].y;
        p[i].id = i;
    
    sort(p + 1, p + 3 * n + 1);
    for(int i = 1; i <= 3 * n; i += 3) 
        cout << p[i].id << ' ' << p[i + 1].id << ' ' << p[i + 2].id << '\\n';
    


int main() 
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    int T; cin >> T;
    while(T--) run();
    return 0;

D - Distinct Values

题意:
构造字典序最小的序列,满足在给出的区间中数字各不重复。

思路:

  • 对于每个位置,能影响它的区间显然从左端点最远起。
  • 发现随着位置的不断增加,最远位置具有单调性。
  • 那么利用\\(set\\)随便搞搞就行了,\\(set\\)里面的就插入可以用的数,每次取最小的就行。

比赛的时候写了个权值线段树求区间\\(mex\\)的做法,维护所有权值最后出现的最小值就行,然后贪心选权值。还是搞复杂了= =思维还是不够灵活。


Code

#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 1e5+5,MAXM = 1e6+5,MOD = 100003,INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define vii vector<pii>
#define vi vector<int>
using namespace std;
struct line
    int l,r;
    bool operator <(const line &rhs)const
        return l==rhs.l?r>rhs.r:l<rhs.l;
    
a[MAXN];
int t,n,m,ans[MAXN],mn[MAXN<<2];
void build(int o,int l,int r)
    mn[o]=0;
    if(l==r)return ;
    int m=mid;
    build(lson);build(rson);

int ask(int o,int l,int r,int L)
    if(l==r)return l;
    int m=mid;
    if(mn[o<<1]<L)return ask(lson,L);
    return ask(rson,L);

inline void pushUp(int o)
    mn[o]=min(mn[o<<1],mn[o<<1|1]);

void upd(int o,int l,int r,int v,int p)
    if(l==r)
        mn[o]=p;
        return ;
    
    int m=mid;
    if(v<=m)upd(lson,v,p);
    else upd(rson,v,p);
    pushUp(o);

int main()
    ios::sync_with_stdio(false);
    //freopen("../A.in","r",stdin);
    //freopen("../A.out","w",stdout);
    cin>>t;
    while(t--)
        cin>>n>>m;
        for(int i=1;i<=m;i++)cin>>a[i].l>>a[i].r;
        sort(a+1,a+1+m);
        build(1,1,n);
        int pre=0;
        for(int i=1;i<=n;i++)ans[i]=1;
        for(int i=1;i<=m;i++)
            if(a[i].r <= pre)continue;
            for(int j=pre+1;j<=a[i].r;j++)
                ans[j] = ask(1,1,n,a[i].l);
                upd(1,1,n,ans[j],j);
            
            pre=a[i].r;
        
        for(int i=1;i<=n;i++)cout<<ans[i]<<" \\n"[i==n];
    
    return 0;

G - Chiaki Sequence Revisited

题意:
定义:
\\[ a_n=\\left\\ \\beginaligned &1&n=1,2\\&a_n-a_n-1+a_n-1-a_n-2&n > 2 \\endaligned \\right. \\]
先求\\(\\sum_i=1^na_i\\)\\(n\\leq 10^18\\)

思路:
懒得写啦,有博客写得很好:传送门
找规律的时候想到二进制基本规律就出来了,核心就是找到出现次数的规律就行。
代码有些细节,因为根据规律,\\(1\\)只会出现一次,但实际上会出现两次,所以我们将\\(n\\)偏移一下即可。另外注意我们找\\(x\\)的时候,要找\\(f(x)\\leq a_n\\)的最大\\(x\\),这里有个等于是为了方便处理数据较小时的情况。
详见代码:


Code

#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 5, MOD = 1e9 + 7, inv2 = (MOD + 1) / 2;

ll n;

ll calc(ll k) 
    if(k <= 1) return k;
    return calc(k / 2) + k;


void run() 
    cin >> n; --n;
    ll l = n / 2 - 50, r = n / 2 + 50, mid;
    while(l < r) 
        mid = (l + r) >> 1;
        if(calc(mid) > n) r = mid;
        else l = mid + 1;
    
    ll p = r - 1;
    ll res = (n - calc(p)) % MOD;
    ll ans = 0;
    for(ll d = 1, c = 1; ; d <<= 1, ++c) 
        if(d > p) break;
        ll s = d % MOD;
        ll k = ((p - d) / (2 * d) + 1) % MOD;
        ll e = (s + (k - 1) * 2ll % MOD * d % MOD) % MOD;
        ans = (ans + c * (s + e) % MOD * k % MOD * inv2 % MOD) % MOD;
    
    ans += (res * ((p + 1) % MOD) % MOD + 1) % MOD;
    ans %= MOD;
    cout << ans << '\\n';


int main() 
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    int T; cin >> T;
    while(T--) run();
    return 0;

H - RMQ Similar Sequence

题意:
定义\\(RMQ(A, l, r)\\)表示\\(A\\)数组中\\([l,r]\\)区间最大值的位置。
现在给出序列\\(A\\),然后随机生成一个序列\\(B\\)\\(B\\)中的每个值都在\\(0\\)\\(1\\)之间。问满足对于\\(1\\leq l\\leq r\\leq n\\),都有\\(RMQ(A, l, r)=RMQ(B,l ,r)\\)\\(B\\)序列的期望权值和为多少。

思路:

  • 显然题目中给出的\\(RMQ(A, l, r)=RMQ(B, l, r)\\)则为两颗笛卡尔树同构,那么求出\\(A\\)的笛卡尔树,\\(B\\)的笛卡尔树形态也确定了。
  • \\(B\\)随机生成的一个实数的期望值为\\(\\frac12\\),那么期望和为\\(\\fracn2\\),接下来主要任务就是求树同构的概率。
  • 我们可以认为\\(B\\)序列中的两两数各不相同,因为出现重复的数可以认为概率为\\(0\\),那么这就相当于一个排列,现在要将这个排列挂在一颗树上并且满足大根堆的性质。
  • 总的方案数为\\(n!\\),但是对于一颗子树而言,其最大值为根的情况只有\\(\\frac1sz_i*n!\\);因为以每个结点为根的方案数是独立的,那么满足条件的总的方案数则为\\(\\fracn!\\prod sz_i\\)。概率就很好求了~

代码如下:


Code

#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
//#define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e6 + 5, MOD = 1e9 + 7;

int n;
int a[N];
pii b[N];

ll qpow(ll a, ll b) 
    ll ans = 1;
    while(b) 
        if(b & 1) ans = ans * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    
    return ans;


struct Cartesian_Tree
    struct node
        int id, val, fa;
        int son[2];
        node()
        node(int id, int val, int fa) : id(id), val(val), fa(fa) 
            son[0] = son[1] = 0;
        
    tr[N];
    int rt;
    void init() 
        tr[0] = node(0, 1e9, 0);
    
    void build(int n, int *a) 
        for(int i = 1; i <= n; i++) tr[i] = node(i, a[i], 0);
        for(int i = 1; i <= n; i++) 
            int k = i - 1;
            while(tr[k].val < tr[i].val) k = tr[k].fa;
            tr[i].son[0] = tr[k].son[1];
            tr[k].son[1] = i;
            tr[i].fa = k;
            tr[tr[i].son[0]].fa = i;
        
        rt = tr[0].son[1];
    
    int dfs(int u) 
        if(!u) return 0;
        int lsz = dfs(tr[u].son[0]);
        int rsz = dfs(tr[u].son[1]);
        b[tr[u].id].fi = lsz;
        b[tr[u].id].se = rsz;
        return lsz + rsz + 1;
    
CT;
void run() 
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    CT.init();
    CT.build(n, a);
    CT.dfs(CT.rt);
    ll res = 2;
    for(int i = 1; i <= n; i++) 
        res = res * (b[i].fi + b[i].se + 1) % MOD;
    
    res = qpow(res, MOD - 2) * n % MOD;
    cout << res << '\\n';


int main() 
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
#ifdef Local
    freopen("../input.in", "r", stdin);
    freopen("../output.out", "w", stdout);
#endif
    int t; cin >> t;
    while(t--) run();
    return 0;

K - Time Zone

模拟。


Code

#include <bits/stdc++.h>
using namespace std;

template <class T, int z>
struct A
    T data[z];
    int n;
    A():n(0) 
    T& operator[](int q) return data[q];
    inline void push(T x) data[n++]=x;
    inline T& pop() return data[--n];
;
char x[10];
int main() 
    int T; scanf("%d", &T);
    while(0<T--) 
        int a,b; scanf("%d%d", &a, &b);
        scanf("%s",x);
        double d; sscanf(x+3,"%lf", &d);
        a-=8; int e=(int)round(d*60);

        a+=e/60,b+=e-(e/60*60);

        while(b>=60) 
            a++,b-=60;
        
        while(b<0) 
            b+=60,a--;
        

        while(a>=24) a-=24;
        while(a<0) a+=24;
        printf("%02d:%02d\\n", a,b);
    
    return 0;

以上是关于2018 Multi-University Training Contest 1的主要内容,如果未能解决你的问题,请参考以下文章

2018 Multi-University Training Contest 2

2018 Multi-University Training Contest 9

2018 Multi-University Training Contest 4

2018 Multi-University Training Contest 4

2018 Multi-University Training Contest 3

2018 Multi-University Training Contest 8