Educational Codeforces Round 33 (Rated for Div. 2) 题解

A.每个状态只有一种后续转移,判断每次转移是否都合法即可。

#include <iostream>
#include <cstdio>
using namespace std;
int a[1001], n;
bool check(int x){
    if(a[1] == x) return false;
    int l = a[1], r = x, s = 6-a[1]-x;
    for(int i = 2; i <= n; i++){
        if(a[i] == s) return false;
        l = a[i]; r = s; s = 6-l-r;
    }
    return true;
}

int main()
{
    cin>>n;
    for(int i = 1; i <= n; i++) cin>>a[i];
    int f = 0;
    if(check(3)) f = 1;
    if(f == 1) printf("YES");
    else printf("NO");
    return 0;
}

B.按照题意枚举约数判断即可。

#include <iostream>
using namespace std;
int n;
bool check(int x){
     for(int i = 1; i <= 100; i++){
        if( ((1<<i) - 1)*(1<<(i-1)) == x) return true;
        if( ((1<<i) - 1)*(1<<(i-1)) > x) return false;
    }
}
int a[1010];
int main()
{
    int ans = 0, temp = 0, last = 0, L;
    cin>>n;
    for(int i = 1; i <= n; i++){
        if(n%i == 0)
            if(check(i))
                ans = max(ans, i);
    }
    cout<<ans<<endl;
}

C.先用并查集并起来,然后瞎搞即可。

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 1e5 + 100;
int a[maxn], f[maxn], v[maxn], s[maxn], M[maxn], x, y;
int find(int x) { return x == f[x] ? f[x] : f[x] = find(f[x]); }
int main()
{
    long long ans = 0;
    int n, m;
    cin>>n>>m;
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        f[i] = i;
        v[i] = a[i];
    }
    for(int i = 1; i <= m; i++){
        scanf("%d %d", &x, &y);
        f[find(x)] = find(y);
    }
    for(int i = 1; i <= n; i++){
        v[find(i)] = min(v[find(i)], v[i]);
    }
    for(int i = 1; i <= n; i++){
        if(M[find(i)] == 0){
            M[find(i)] = 1;
            ans += (long long)v[find(i)];
        }
    }
    cout<<ans<<endl;
}

D.一道题意非常困难的题目orz。

题意大概就是:给你每天的金钱变化,你可以选择加任意数量的钱,但是需要保证每个时刻的钱数不超过d,而且当金钱变化为0时,此时钱的数量必须非负(check一次)

最后还是读错题了。

这道题贪心是对的,先考虑画出价格的折线图。

在第i天加钱,就相当于抬高i天以后的折线。

贪心地想,反正加更多的钱总是更优

所以如果在第i天加钱,就尽量多的加钱

而能加的钱数就是上限d - Max[i](Max为从i到n的后缀),如果比这个还多,那么后面必定会超

所以就在每个需要加钱的位置都贪心地加钱即可。

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 1e5 + 100;
int a[maxn], n;
long long b[maxn], Max[maxn], d;
int main()
{
    cin>>n>>d;
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    b[0] = 0;
    for(int i = 1; i <= n; i++) b[i] = b[i-1] + a[i];
    Max[n] = b[n];
    for(int i = n-1; i >= 1; i--) Max[i] = max(b[i], Max[i+1]);
    int t = 0, f = 0;
    if(Max[1] > d){
        f = 1;
    } else {
        long long h = 0;
        for(int i = 1; i <= n; i++){
            if(a[i] == 0 && b[i] + h < 0){
                h = d - Max[i];
                t++;
                if(b[i] + h < 0) { f = 1; break; }
            }
        }
    }
    if(f){
        printf("-1");
    } else printf("%d", t);
    return 0;
}

E.给你一个数x,你需要把它拆成y个数的乘积,只要对应位置的yi有一个不同,方案就是不同的,问有多少种方案(负数也算)

首先考虑只有正数。

把x质因数拆分,然后对于每个质因数

你需要把它分配个y个数,比如24 = 3*2^3

其中就需要把3个2分配给y个数,实际上就是y个不同的盒子,3个球,问有多少种方案

答案就是C(y+3-1, y-1)

然后每个质因数乘起来就可以了。

如果是负数,那么其实就是每个正数的方案选出2个变成负数

就是C(n, 0) + C(n, 2) + ....  = 2^(n-1)

最后都乘起来就可以了。

这题很蠢的是数组越界了,因为n+m大于1e6,但是没有预处理到(以及找质因数的写法一定要注意)

#include <iostream>
#include <vector>
#include <cstdio>
#define mp make_pair
#define fi first
#define se second
using namespace std;
vector< pair<int, int> > V;
const int MOD = 1e9 + 7;
typedef long long LL;
LL f[1100010], invf[1100010];
LL mypow(LL a, LL b){
    LL ans = 1; for(; b; b >>= 1, (a *= a) %= MOD) if(b&1) (ans *= a) %= MOD; return ans;
}
LL C(LL n, LL m){
    return f[n]*invf[m]%MOD*invf[n-m]%MOD;
}
int T, x, y;
int main()
{
    cin>>T;
    f[0] = 1;
    for(int i = 1; i <= 1100000; i++) f[i] = f[i-1]*i%MOD;
    invf[1100000] = mypow(f[1100000], MOD-2);
    for(int i = 1100000-1; i >= 0; i--) invf[i] = (invf[i+1]*(i+1))%MOD;
    while(T--){
        scanf("%d %d", &x, &y);
        int t = x;
        long long ans = 1;
        for(int i = 2; i*i <= x; i++){
            if(t % i == 0){
                int temp = 0;
                while(t % i == 0) temp++, t /= i;
                (ans *= C(temp+y-1, y-1) ) %= MOD;
            }
        }
        if(t != 1) (ans *= y) %= MOD;
        (ans *= mypow(2, y-1)) %= MOD;
        printf("%I64d
", ans);
    }
    return 0;
}

F.给定一棵树,每次询问x构成的子树内,距离x结点小于等于k的点中值最小的是多少。强制在线

这题我是用可持久化线段树+维护dfs序做的,复杂度是nlogn。不过也有很暴力的线段树维护dfs序nlognlogn做法

具体思想就是可持久化线段树的结点对应是dfs序

插入的时间是按深度排序。

那么询问x,k

就是问插入时间从1~deep[x]+k所构成的树中,x的子树中结点最小的值是多少。

然后维护dfs序,就是查询tree(deep[x] + k, ein[x], eout[x])这个区间的值。

大概就是这样,没什么很坑的地方。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 2e5 + 500;
struct Node{
    Node *l, *r;
    int v;
    void maintain(){
        v = min(l->v, r->v);
    }
}pool[maxn*16], *root[maxn], *null;
int pooltot = 0;
Node *newnode(){
    Node* temp = &pool[pooltot++];
    temp->l = temp->r = null; temp->v = 1e9; //
    return temp;
}
void init(){
    null = new Node();
    null->l = null->r = null;
    null->v = 1e9; //
}
void Insert(Node *&o, Node *o2, int l, int r, int k, int v){
    if(o == null) o = newnode();
    if(l == r) { o->v = min(v, o2->v); return; } //
    int mid = (l+r)/2;
    if(k <= mid) {
        Insert(o->l, o2->l, l, mid, k, v);
        o->r = o2->r;
    } else {
        Insert(o->r, o2->r, mid+1, r, k, v);
        o->l = o2->l;
    }
    o->maintain();
}
int Query(Node *o, int l, int r, int L, int R){
    if(o == null) return 1e9; //
    if(L <= l && r <= R) return o->v;
    int mid = (l+r)/2, ans = 1e9; //
    if(L <= mid) ans = min(ans, Query(o->l, l, mid, L, R)); //
    if(R > mid) ans = min(ans, Query(o->r, mid+1, r, L, R)); //
    return ans;
}
int tot = 0, Maxd, timer = 0;
int ein[maxn], deep[maxn], eout[maxn], a[maxn], lastd[maxn];
vector<int> D[maxn], G[maxn];
void dfs(int x, int fa, int d){
    ein[x] = ++tot;
    deep[x] = d;
    Maxd = max(d, Maxd);
    D[d].push_back(x);
    for(auto to : G[x]){
        if(to == fa) continue;
        dfs(to, x, d+1);
    }
    eout[x] = tot;
}
int n, r, x, y, q;
int main(){
    init();
    cin>>n>>r;
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for(int i = 1; i < n; i++){
        scanf("%d %d", &x, &y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    dfs(r, r, 1);
    for(int i = 0; i <= n + 100; i++) root[i] = newnode();
    for(int i = 0; i <= Maxd; i++){
        for(auto x : D[i]){
            Insert(root[timer+1], root[timer], 1, tot, ein[x], a[x]);
            timer++;
        }
        lastd[i] = timer;
    }
    scanf("%d", &q);
    int last = 0;
    while(q--){
        scanf("%d %d", &x, &y);
        x = (x+last)%n + 1;
        y = (y+last)%n;
        printf("%d
", last = Query(root[lastd[min(Maxd, deep[x]+y)]], 1, tot, ein[x], eout[x]));
    }
}
原文地址:https://www.cnblogs.com/Saurus/p/7891315.html