Codeforces Round #549 (Div. 2) A-F题解

Codeforces Round #549(div2)

    • A-The Doors(暴力)
    • B-Nirvana(dp+贪心)
    • C-Queen(暴力)
    • D-The Beatles(数论gcd)
    • E-Lynyrd Skynyrd(dp+倍增)
    • F-U2(计算几何凸壳)

A-The Doors(暴力)

题意:n扇门,左边的为0,右边的为1,问你最快什么时候可以关闭所有左边或右边的门

思路:输出最右边0或1位置的较小值即可

AC_code:

#include
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9+7;
const LL INF = 1e9+7;
const int base = 131;
const double eps = 0.000001;
const double pi = 3.1415926;

int n;
int a[200010];

vector<int > v1, v2;

int main() {
#ifndef ONLINE_JUDGE
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif
    while(~scanf("%d", &n)) {
        int idl = 1, idr = 1;
        for(int i = 1; i <= n ;i ++) {
            scanf("%d", &a[i]);
            if(a[i]) idl = max(idl, i);
            else idr = max(idr, i);
        }
        printf("%d\n", min(idl, idr));
    }
#ifndef ONLINE_JUDGE
    cout <<"It costs " <<clock() <<" ms\n";
#endif
    return 0;
}

B-Nirvana(dp+贪心)

题意:输出1~n范围为某个数的每一位数乘积的最大值

思路:从后往前 d p , d p [ i ] dp,dp[i] dpdp[i]表示第i个位置到最后一位可以得到的最大乘积,每个位置可以减小1或者不变,贪心的想法,如果第i位的数字减少1,那么这个位置后面的数都变为9是最优的,如果不减小那么就乘以 d p [ i − 1 ] dp[i-1] dp[i1],再取个 m a x max max就可以得到 d p [ i ] dp[i] dp[i]了,最后输出 d p [ 0 ] dp[0] dp[0]即可,特判下首位减为0的情况即可。

AC_code:

#include
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9+7;
const LL INF = 1e9+7;
const int base = 131;
const double eps = 0.000001;
const double pi = 3.1415926;

int n;
int dp[20], c[20];
vector<int> v;

void init() { //预处理9的幂次
    c[0] = 1;
    for(int i = 1; i <= 9; i ++) {
        c[i] = c[i-1]*9;
    }
}

int main() {
#ifndef ONLINE_JUDGE
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif
    init();
    while(~scanf("%d", &n)) {
        mem0(dp);
        v.clear();
        while(n) {
            v.push_back(n%10);
            n /= 10;
        }
        dp[0] = v[0];
        int sz = v.size();
        for(int i = 1; i < sz; i ++) {
            int k;
            if(i == sz-1) k = 1;
            else k = 0;
            dp[i] = max(dp[i-1]*v[i], max(k, v[i]-1)*c[i]);
        }
        printf("%d\n", dp[sz-1]);
    }
#ifndef ONLINE_JUDGE
    cout <<"It costs " <<clock() <<" ms\n";
#endif
    return 0;
}

C-Queen(暴力)

题意:给你一棵有根树,和他的节点的信息,第 i i i个节点的父节点为 p [ i ] , c [ i ] p[i],c[i] p[i]c[i]表示这个节点是否尊敬他的祖先,让你按编号从小到大删除既不尊敬祖先,有不被所有儿子尊敬的节点

思路:直接纪录下节点的信息,标记下这个节点是否要删,最后从大到小输出即可

AC_code:

#include
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9+7;
const LL INF = 1e9+7;
const int base = 131;
const double eps = 0.000001;
const double pi = 3.1415926;

int n;
int p[100010], c[100010], ok[100010];

vector<int> v;

int main() {
#ifndef ONLINE_JUDGE
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif
    while(~scanf("%d", &n)) {
        mem0(ok);
        v.clear();
        for(int i = 1; i <= n; i ++) {
            scanf("%d%d", &p[i], &c[i]);
            if(c[i] == 0 && p[i] != -1) {
                ok[p[i]] = 1;
            }
        }
        for(int i = 1; i <= n; i ++) {
            if(!ok[i] && c[i]) v.push_back(i);
        }
        if(v.size() == 0) printf("-1");
        else {
            for(int i = 0; i < v.size(); i ++) {
                printf("%d%c", v[i], i == v.size()-1 ? '\n' : ' ');
            }
        }
    }
#ifndef ONLINE_JUDGE
    cout <<"It costs " <<clock() <<" ms\n";
#endif
    return 0;
}

D-The Beatles(数论gcd)

题意:给你一个大小为 n ∗ k n * k nk的环,每1开始,每过k个位置有一个餐馆,一个人从s点出发,每走l的距离停一次,知道他再次回到 s s s点,但是他忘记了 s s s l l l,只知道 s s s点离最近的餐馆距离为a, s + l s+l s+l位置点离最近的餐馆距离为 b b b,让你求出他再次回到s点停的最小和最大次数。

思路:随便满足条件的位置作为起点 s s s,让后我们可以求出所有第一次 ( s + l ) (s+l) (s+l)可能到的的位置,这样就可以枚举出所有的 l l l,那么答案就为最小和最大的 ( n ∗ k ) / g c d ( l , k ∗ n ) (n * k)/gcd(l, k*n) (nk)/gcd(l,kn)

AC_code:

#include
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 1e9+7;
const LL INF = 1e9+7;
const int base = 131;
const double eps = 0.000001;
const double pi = 3.1415926;

LL n, k;
int a, b;
set<LL> st;
set<LL>::iterator it;

LL gcd(LL a, LL b) {
    if(a%b == 0) return b;
    return gcd(b, a%b);
}

int main() {
#ifndef ONLINE_JUDGE
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif
    while(~scanf("%d%d", &n, &k)) {
        scanf("%d%d", &a, &b);
        st.clear();
        for(int i = 0; i < n; i ++) {
            st.insert((k*i)+1-b);
            st.insert((k*i)+1+b);
        }
        LL s = 1+a;
        LL ans1 = 1e15, ans2 = 0;
        for(it = st.begin(); it != st.end(); it ++) {
            LL len;
            LL now = *it;
            if(now > s) len = now - s;
            if(now < s) len = now + n*k - s;
            if(now == s) {
                len = 0;
                ans1 = min(ans1, (n*k)/gcd(len, n*k));
                ans2 = max(ans2, (n*k)/gcd(len, n*k));
                len = n*k;
                ans1 = min(ans1, (n*k)/gcd(len, n*k));
                ans2 = max(ans2, (n*k)/gcd(len, n*k));
            }
            //printf("%lld\n", len);
            ans1 = min(ans1, (n*k)/gcd(len, n*k));
            ans2 = max(ans2, (n*k)/gcd(len, n*k));
        }
        printf("%lld %lld\n", ans1, ans2);
    }
#ifndef ONLINE_JUDGE
    cout <<"It costs " <<clock() <<" ms\n";
#endif
    return 0;
}

E-Lynyrd Skynyrd(dp+倍增)

题意:给你一个 1   n 1~n 1 n的全排列,和一个大小为 m m m的数组,有 q q q次查询 [ l i , r i ] [li, ri] [li,ri],询问数组在这个区间里是否存在一个子序列是全排列的某个 c y c l i c cyclic cyclic(一个循环)。

思路:因为是全排列,所以每个数都是不一样的,所以我们知道当前位置的数是多少,那么排在它前面的数一定是固定的,所以我们先预处理出全排列中每个数的前继点: p r e [ a [ i ] ] = a [ i − 1 ] pre[a[i]] = a[i-1] pre[a[i]]=a[i1]然后从前往后 d p dp dp一遍, d p [ i ] [ 0 ] dp[i][0] dp[i][0]表示,数组1~位置i(必要)得到原排列的最大长度,那么初始化 d p [ i ] dp[i] dp[i]为1,那么 d p [ i ] [ 0 ] + = d p [ i d x [ p r e [ b [ i ] ] ] ] dp[i][0] += dp[idx[pre[b[i]]]] dp[i][0]+=dp[idx[pre[b[i]]]](离b[i]最近的b[i]的前继点的最大长度,贪心的想法最近肯定是最优的),并且纪录下最近的这个前继点的位置为位置i的父亲 f a [ i ] = i d x [ p r e [ b [ i ] ] ] fa[i] = idx[pre[b[i]]] fa[i]=idx[pre[b[i]]],这样进行一遍dp后发现只能判断某个位置是否能得到全排列,并不能判断区间,这时候需要我们再一次从前往后更新一遍 d p dp dp数组,得到1~位置i(不必要)内可以得到的最大长度, d p [ i ] [ 0 ] = m a x ( d p [ i ] [ 0 ] , d p [ i − 1 ] [ 0 ] ) dp[i][0] = max(dp[i][0], dp[i-1][0]) dp[i][0]=max(dp[i][0],dp[i1][0]),然后 d p [ i ] [ 1 ] dp[i][1] dp[i][1]纪录得到全排列长度为 d p [ i ] [ 1 ] dp[i][1] dp[i][1]的第一个数最右边的位置,首先我们想到根据 f a fa fa数组一个一个往前遍历得到距离它长度为 n n n的位置,但是发现复杂度太大 O ( n ) O(n) O(n),我们联想 L C A LCA LCA求父亲的过程,发现可以倍增来实现 O ( l o g n ) O(logn) O(logn),写完后看别人都是 d f s dfs dfs写的,有兴趣的童鞋也可以去看下。

AC_code:(187ms还是挺快的)

#include
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 998244353;
const LL INF = 1e9+7;
const int base = 131;
const double eps = 0.000001;
const double pi = 3.1415926;

int n, m, q;
int a[200010], b[200010], pre[200010], fa[200010];
int dp[200010][2], vis[200010], f[200010][20], c[200010];

void ST() {
    for(int i = 1; i <= m; i ++) { //f[i][j]表示距离i为2^j的父亲位置
        f[i][0] = fa[i];
    }
    for(int i = 1; (1<<i) <= n; i ++) {
        for(int j = 1; j <= m; j ++) {
            f[j][i] = f[f[j][i-1]][i-1];
        }
    }
    int cnt = 0;
    for(int i = 1; i <= n; i *= 2){
        c[i] = cnt ++;
    }
}

int query(int i, int j) {
    int ans = i;
    while(j) {
        int k = lowbit(j);
        ans = f[ans][c[k]];
        j -= k;
    }
    return ans;
}

int main() {
#ifndef ONLINE_JUDGE
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif
    while(~scanf("%d%d%d", &n, &m, &q)) {
        mem0(dp);
        mem0(vis);
        for(int i = 0; i < n; i ++) {
            scanf("%d", &a[i]);
            if(i > 0) pre[a[i]] = a[i-1];
        }
        pre[a[0]] = a[n-1];
        for(int i = 1; i <= m; i ++) {
            fa[i] = i;
            scanf("%d", &b[i]);
            vis[b[i]] = i; //最近前继点的位置
            dp[i][0] = 1;
            int k = pre[b[i]];
            if(vis[k]) { //存在前继点
                int id = vis[k];
                dp[i][0] += dp[id][0];
                fa[i] = id;
            }
        }
        ST();//倍增求父亲
        int l, r;
        int idx = -1;
        for(int i = 1; i <= m; i ++) {
            if(dp[i][0] >= n) dp[i][1] = query(i, n-1);//查询父亲位置
            dp[i][0] = max(dp[i][0], dp[i-1][0]);
            if(dp[i][0] >= n) {
                idx = max(idx, dp[i][1]);//更新最右位置
                dp[i][1] = idx;
            }
        }
        while(q --) {
            scanf("%d%d", &l, &r);
            if(dp[r][0] >= n && dp[r][1] >= l) {
                printf("1");
            }else printf("0");
        }
        printf("\n");
    }
#ifndef ONLINE_JUDGE
    cout <<"It costs " <<clock() <<" ms\n";
#endif
    return 0;
}

F-U2(计算几何凸壳)

题意:给你n个点,让你找出一共可以构成多少不同的抛物线 ( y = x 2 + b ∗ x + c ) (y = x^2+b * x+c) y=x2+bx+c使得所有的点不在这个抛物线的上方。

思路:写的时候完全没有思路,题解上是这么说的:我们首先可以知道对于所有的点来说,有 y < = x 2 − b ∗ x + c y <= x^2 - b * x + c y<=x2bx+c类似于进行一次坐标变换,令 x = x , y = y − x 2 x = x,y = y-x^2 x=xy=yx2,就可以得到, y < = b ∗ x + c y <= b * x + c y<=bx+c,然后发现就是一条直线使得变换左边后的所有的点都在这条直线的下方,就是求一个凸包的上壳。

AC_code:

#include
#pragma GCC optimize(2)
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define finf(a, n) fill(a, a+n, INF);
#define lowbit(x) x&-x
#define fuck(x) cout<<#x<<" = "<
#define ios ios::sync_with_stdio(false);
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, pair<int, LL> > LLppar;
typedef pair<int, int> par;
typedef pair<LL, int> LLpar;
const LL mod = 998244353;
const LL INF = 1e9+7;
const int base = 131;
const double eps = 0.000001;
const double pi = 3.1415926;

int n;

struct point {
    LL x, y;

    point(){}
    point(LL x, LL y):x(x),y(y){}

    friend LL operator * (const point &a, const point &b) {
        return a.y*b.x - a.x*b.y;
    }

    friend point operator - (const point &a, const point &b) {
        return point(a.x-b.x, a.y-b.y);
    }

    bool operator < (const point &a) const {
        if(x == a.x) return y < a.y;
        return x < a.x;
    }
};

vector<point> v, st;

void convex_hull() {
    sort(v.begin(), v.end()); //点排序
    for(int i = n-1; i >= 0; i --) { //以v[0]为原点坐标变换
        v[i] = v[i] - v[0];
    }
    for(int i = 0; i < v.size(); i ++) {
        int tot = st.size();
        while(tot > 1 && (v[i]-st[tot-2])*(st[tot-1]-st[tot-2]) >= 0) { //判断凸壳
            tot--;
            st.pop_back();
        }
        st.push_back(v[i]);
    }
}

int main() {
#ifndef ONLINE_JUDGE
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif
    LL x, y;
    while(~scanf("%d", &n)) {
        v.clear();
        st.clear();
        for(int i = 0; i < n; i ++) {
            scanf("%lld%lld", &x, &y);
            v.push_back(point(x, y-x*x));
        }
        convex_hull();
        int ans = 0;
        int sz = st.size();
        for(int i = 0; i < sz-1; i ++) {
            if(st[i].x != st[i+1].x) ans ++; //抛物线横坐标不相同
        }
        printf("%d\n", ans);
    }
#ifndef ONLINE_JUDGE
    cout <<"It costs " <<clock() <<" ms\n";
#endif
    return 0;
}

你可能感兴趣的:(codeforce)