2020牛客寒假算法基础集训营第二场

C. 算概率

题目虽然给我们的是概率取模之后的结果,但是我们可以直接把它看作概率

dp[i][j] 代表前 i 道题正好做对 j 道的概率

dp[0][0] : 0道题做对 0 题的概率就是 1

状态转移方程就应该是:

dp[i][j]   = dp[i-1][j] * (1 -p[i] + mod ) % mod + dp[i-1][j] * p[i] % mod

       (第 i 道题做错的情况)                  (第 i 道题做对的情况)

#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <cstdio>
#include <iomanip>
#include <time.h>

#define LL long long
#define INF 0x3f3f3f3f
#define ls nod<<1
#define rs (nod<<1)+1

using namespace std;

const int maxn = 3e5 + 10;
const LL mod = 1e9 + 7;

LL dp[2010][2010];
LL p[2010];

int main() {
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++) {
        cin >> p[i];
    }
    dp[0][0] = 1;
    for (int i = 1;i <= n;i++) {
        dp[i][0] = (dp[i-1][0]*(1-p[i]+mod))%mod;
        for (int j = 1;j <= n;j++) {
            dp[i][j] = ((dp[i-1][j]*(1-p[i]+mod))%mod + (dp[i-1][j-1]*p[i])%mod)%mod;
        }
    }
    for (int i = 0;i <= n;i++) {
        cout << dp[n][i] << " ";
    }
    return 0;
}

E. 做计数

我们对所给的式子两边进行平方,那么我们可以知道 i*j 必须是完全平方数 ,然后题目又告诉了 i*j <= n 

所以我们只需要去遍历[1~n]范围内的完全平方数,去把这些完全平方数的因子数全部加起来就可以了

#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <cstdio>
#include <iomanip>
#include <time.h>

#define LL long long
#define INF 0x3f3f3f3f
#define ls nod<<1
#define rs (nod<<1)+1



using namespace std;

const int maxn = 1e4 + 10;
const LL mod = 1e9 + 7;

bool check(int n) {
    int m=floor(sqrt(n)+0.5);
    if(m*m==n)
        return true;
    return false;
}


LL Getn(LL x){
    LL ret = 1;
    for(LL i = 2; i * i <= x; i++){
        if(x % i == 0){
            LL a = 0;
            while(x % i == 0){
                x /= i;
                a++;
            }
            ret = ret * (a + 1);
        }
    }
    if(x > 1){
        ret *= 2;
    }
    return ret;
}

int main() {
    int n;
    cin >> n;
    LL cnt = 0;
    for (int i = 1;i <= n;i++) {
        if (check(i)) {
            cnt += Getn(i);
        }
        else
            continue;
    }
    printf("%lld\n",cnt);
    return 0;
}

F. 拿物品

#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <cstdio>
#include <iomanip>
#include <time.h>

#define LL long long
#define INF 0x3f3f3f3f
#define ls nod<<1
#define rs (nod<<1)+1

using namespace std;

const int maxn = 2e5 + 10;
const LL mod = 1e9 + 7;

int a[maxn],b[maxn];
struct Node {
    int id;
    LL val;
}c[maxn];

bool cmp(Node a,Node b) {
    return a.val > b.val;
}

vector<int> vec[2];
int main() {
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++)
        cin >> a[i];
    for (int i = 1;i <= n;i++) {
        cin >> b[i];
        c[i].id = i;
        c[i].val = a[i] + b[i];
    }
    sort(c+1,c+1+n,cmp);
    for (int i = 1;i <= n;i++) {
        vec[i%2].push_back(c[i].id);
    }
    for (auto i:vec[1])
        cout << i << " ";
    cout << endl;
    for (auto i:vec[0])
        cout << i << " ";
    cout << endl;
    return 0;
}

H.施魔法

 dp[i] 代表前 i 个元素最少需要的魔力

当 i >= k 的时候:

我们可以考虑把这个区间拆成两个区间 [1~j] 和 [j+1~i],然后我们知道 大区间的极差 = 它拆成的小区间的极差之和

那么我们就可以去枚举前面这个区间的所有情况去找到一个最小的魔力

很可惜的是这个算法的时间复杂度是 O(N^2) 

我们还需要进一步的去优化这个算法

 这里就有点类似贪心的想法,我们每次都取最优的,所以我们需要取维护dp[i] - a[i+1] 这个值最小的一个下标

#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <cstdio>
#include <iomanip>
#include <time.h>

#define LL long long
#define INF 0x3f3f3f3f
#define ls nod<<1
#define rs (nod<<1)+1

using namespace std;

const int maxn = 3e5 + 10;
const LL mod = 1e9 + 7;

int dp[maxn];
int a[maxn],b[maxn];

int main() {
    int n,k;
    cin >> n >> k;
    for (int i = 1;i <= n;i++)
        cin >> a[i];
    sort(a+1,a+1+n);
    memset(dp,INF, sizeof(dp));
    for (int i = 0;i < k;i++)
        dp[i] = 0;
    for (int i = k;i <= n;i++) {
        int pre = b[i-k];
        dp[i] = dp[pre] + a[i] - a[pre+1];
        if (dp[i]-a[i+1] < dp[b[i-1]]-a[b[i-1]+1])
            b[i] = i;
        else
            b[i] = b[i-1];
    }
    cout << dp[n] << endl;
    return 0;
}

I.建通道

 枚举二进制就可以了

#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <cstdio>
#include <iomanip>
#include <time.h>

#define LL long long
#define INF 0x3f3f3f3f
#define ls nod<<1
#define rs (nod<<1)+1

using namespace std;

const int maxn = 3e5 + 10;
const LL mod = 1e9 + 7;

set<int> st[35];
int f[35][2];

int main() {
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++) {
        int val;
        cin >> val;
        st[0].insert(val);
        f[0][val&1]++;
    }
    LL cnt = 0;
    for (int i = 0;i <= 30;i++) {
        if (f[i][0] && f[i][1]) {
            cnt = (st[i].size() - 1) * (1 << i);
            break;
        }
        for (auto j:st[i]) {
            st[i+1].insert(j >> 1);
            f[i+1][(j >> 1) & 1]++;
        }
    }
    cout << cnt << endl;
    return 0;
}

J. 求函数

对于区间合并的处理还是挺有意思的

#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <cstdio>
#include <iomanip>
#include <time.h>

#define LL long long
#define INF 0x3f3f3f3f
#define ls nod<<1
#define rs (nod<<1)+1

using namespace std;

const int maxn = 3e5 + 10;
const LL mod = 1e9 + 7;


struct segment_tree {
    LL k,b;
}tree[maxn << 2];

LL k[maxn],b[maxn];

void build (int l,int r,int nod) {
    if (l == r) {
        tree[nod].k = k[l];
        tree[nod].b = b[l];
        return ;
    }
    int mid = (l + r ) >> 1;
    build(l,mid,ls);
    build(mid+1,r,rs);
    tree[nod].k = (tree[ls].k * tree[rs].k) % mod;
    tree[nod].b = (tree[ls].b * tree[rs].k + tree[rs].b) % mod;
}

void modify(int l,int r,int k,LL v,LL v1,int nod) {
    if (l == r) {
        tree[nod].k = v;
        tree[nod].b = v1;
        return ;
    }
    int mid = (l + r) >> 1;
    if (k <= mid)
        modify(l,mid,k,v,v1,ls);
    else
        modify(mid+1,r,k,v,v1,rs);
    tree[nod].k = (tree[ls].k * tree[rs].k) % mod;
    tree[nod].b = (tree[ls].b * tree[rs].k  + tree[rs].b)%mod;
}

segment_tree merge(segment_tree a,segment_tree b) {
    return {a.k * b.k % mod,(a.b * b.k + b.b)%mod};
}

segment_tree query(int l,int r,int ql,int qr,int nod) {
    if (ql <= l && qr >= r)
        return {tree[nod].k,tree[nod].b};
    int mid = (l + r ) >> 1;
    if (qr <= mid)
        return query(l,mid,ql,qr,ls);
    if (ql > mid)
        return query(mid+1,r,ql,qr,rs);
    return merge(query(l,mid,ql,qr,ls),query(mid+1,r,ql,qr,rs));
}

int main() {
    int n,m;
    scanf("%d%d",&n,&m);
    for (int i = 1;i <= n;i++)
        scanf("%lld",&k[i]);
    for (int i = 1;i <= n;i++)
        scanf("%lld", &b[i]);
    build(1,n,1);
    while (m--) {
        int opt,x,y,z;
        scanf("%d%d%d",&opt,&x,&y);
        if (opt == 1) {
            scanf("%d",&z);
            modify(1,n,x,y,z,1);
        }
        else {
            segment_tree ans = query(1,n,x,y,1);
            printf("%lld\n",(ans.k + ans.b) % mod);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/-Ackerman/p/12274374.html