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

A、牛牛的DRB迷宫I

简单的数字三角形DP问题

#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;

char mp[55][55];
int n,m;
int dp[55][55];


int main() {
    cin >> n >> m;
    for (int i = 1;i <= n;i++) {
        for (int j = 1;j <= m;j++)
            cin >> mp[i][j];
    }
    dp[1][1] = 1;
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (mp[i][j] == 'R')
            {
                dp[i][j + 1] = (dp[i][j + 1] + dp[i][j]) % mod;
            }
            else if (mp[i][j] == 'D')
            {
                dp[i + 1][j] = (dp[i + 1][j] + dp[i][j]) % mod;
            }
            else
            {
                dp[i + 1][j] = (dp[i + 1][j] + dp[i][j]) % mod;
                dp[i][j + 1] = (dp[i][j + 1] + dp[i][j]) % mod;
            }
        }
    }
    printf("%lld\n", dp[n][m] % mod);
    return 0;
}

B、牛牛的DRB迷宫II

非常有意思的构造题目

我们就构造一个特殊的方阵,它的主对角线上都是B,主对角线下面一条都是R,上面一条都是D,那么假如其他的都是R,此时主对角线上能走到的是不是1 2 4 8

用二进制我们可以拼出所有的数字,所以这种的是肯定是可以构造出答案的。

斜对角线的R对应位置是二进制数,然后只要这一位有的话就可以直接把他变成B。并且让它可以直接走到底部。

最后我只需要统计到达底部的数目之和,也就是进行二进制运算了。非常巧妙

#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;

char s[55][55];
int vis[200];

int main() {
    LL k;
    cin >> k;
    k %= mod;
    if (k == 0) {
        cout << "2 2" << endl;
        cout << "RR" << endl;
        cout << "RR" << endl;
    }
    int cnt = 0;
    while (k) {
        cnt++;
        if (k & 1)
            vis[cnt] = 1;
        k >>= 1;
    }
    for (int i = 1;i <= cnt;i++)
        s[i][i] = 'B';
    for (int i = 1;i <= cnt-1;i++) {
        s[i][i+1] = 'D';
        s[i+1][i] = 'R';
    }
    cnt++;
    for (int i = 1;i <= cnt;i++) {
        if (vis[i]) {
            s[i+1][i] = 'B';
            for (int j = i+2;j <= cnt;j++)
                s[j][i] = 'D';
            for (int j = 1;j <= cnt;j++)
                s[cnt][j] = 'R';
        }
    }
    cout << cnt << " " << cnt << endl;
    for (int i = 1;i <= cnt;i++) {
        for (int j = 1;j <= cnt;j++) {
            if (s[i][j] == 0)
                s[i][j] = 'R';
            cout << s[i][j];
        }
        cout << endl;
    }
    return 0;
}

F、牛牛的Link Power I

我们发现每个“1”对于它本身位置产生的影响贡献为0,而往后面依次产生了0,1,2,3,4,5...的贡献。
然后你可以利用静态维护区间加等差数列的技巧
那当然,这个东西特殊,不用这么一般性的技巧。
对于每个位置的1",假设它的位置为pos,那么直接对a[pos+1]加上1的贡献。(相等于维护一个二次方差)
全部做完以后,做两次前缀和操作,对于每个位置的“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 = 1e5 + 10;
const LL mod = 1e9 + 7;
 
char s[maxn];
int n;
LL sum[maxn],ans;
 
void pre_sum() {
    for (int i = 1;i <= n;i++) {
        sum[i] += sum[i-1];
        if (sum[i] >= mod)
            sum[i] -= mod;
    }
}
 
int main() {
    scanf("%d",&n);
    scanf("%s",s+1);
    for (int i = 1;i <= n;i++) {
        if (s[i] == '1')
            sum[i+1]++;
    }
    pre_sum();
    pre_sum();
    for (int i = 1;i <= n;i++) {
        if (s[i] == '1') {
            ans += sum[i];
            if (ans >= mod)
                ans -= mod;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

G、牛牛的Link Power II

对于每一个 ‘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 = 1e5 + 10;
const LL mod = 1e9 + 7;

char s[maxn];

struct segment_tree {
    LL val;
    LL lazy;
};

struct Segment {
    segment_tree tree[maxn*4];
    void build(int l, int r, int nod) {
        if (l == r) {
            tree[nod].lazy = 0;
            tree[nod].val = 0;
            return;
        }
        int mid = (l + r) >> 1;
        build(l, mid, ls);
        build(mid + 1, r, rs);
        tree[nod].val = tree[ls].val + tree[rs].val;
    }

    void pushdown(int l, int r, int nod) {
        if (tree[nod].lazy) {
            int mid = (l + r) >> 1;
            tree[ls].lazy += tree[nod].lazy;
            tree[rs].lazy += tree[nod].lazy;
            tree[ls].val += (mid - l + 1) * tree[nod].lazy;
            tree[rs].val += (r - mid) * tree[nod].lazy;
            tree[nod].lazy = 0;
        }
    }

    void modify(int l, int r, int ql, int qr, int v, int nod) {
        if (ql <= l && qr >= r) {
            tree[nod].lazy += v;
            tree[nod].val += (r - l + 1) * v;
            return;
        }
        pushdown(l, r, nod);
        int mid = (l + r) >> 1;
        if (ql <= mid)
            modify(l,mid,ql,qr,v,ls);
        if (qr > mid)
            modify(mid+1,r,ql,qr,v,rs);
        tree[nod].val = tree[ls].val + tree[rs].val;
    }

    LL query(int l, int r, int ql, int qr, int nod) {
        if (ql <= l && qr >= r)
            return tree[nod].val;
        pushdown(l,r,nod);
        int mid = (l + r) >> 1;
        LL cnt = 0;
        if (ql <= mid)
            cnt += query(l,mid,ql,qr,ls);
        if (qr > mid)
            cnt += query(mid+1,r,ql,qr,rs);
        return cnt;
    }
}suf,pre;

int main() {
    int n,m;
    scanf("%d",&n);
    scanf("%s",s+1);
    LL ans = 0;
    pre.build(1,n,1);
    suf.build(1,n,1);
    for (int i = 1;i <= n;i++) {
        if (s[i] == '1') {
            ans = (ans + pre.query(1,n,1,i,1) + suf.query(1,n,i,n,1)) % mod;
            if (i!= n)
                pre.modify(1,n,i+1,n,1,1); // 前面对后面的贡献
            if (i!= 1)
                suf.modify(1,n,1,i-1,1,1); // 后面对前面对贡献
        }
    }
    printf("%lld\n",ans);
    scanf("%d",&m);
    while (m--) {
        int opt,x;
        scanf("%d%d",&opt,&x);
        LL pres = pre.query(1,n,1,x,1); //前面对x的贡献
        LL sufs = suf.query(1,n,x,n,1); // 后面对x的贡献
        if (opt == 1) {
            ans = (ans + pres + sufs) % mod;
            if (x+1 <= n)
                pre.modify(1,n,x+1,n,1,1); //
            if (x-1 >= 1)
                suf.modify(1,n,1,x-1,1,1);
        }
        else {
            ans = ((ans - pres - sufs) % mod + mod ) % mod;
            if (x+1 <= n)
                pre.modify(1,n,x+1,n,-1,1);
            if (x-1 >= 1)
                suf.modify(1,n,1,x-1,-1,1);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

H、牛牛的k合因子数

x 的所有因数 - x的所有质因数 - 1 (因数是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 = 2e5 + 10;
const LL mod = 1e9 + 7;

int cnt[maxn];
int visit[maxn];
int n, m;
int cntn[maxn];
int ans[maxn];

int Getn(int x){
    int 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;
}

void Prime(){
    memset(visit, 0, sizeof(visit));          
    for(int i = 2; i <= n; i++){
        if (!visit[i]) {        
            for(int j = 2 * i; j <= n; j += i){
                visit[j] = 1;
                cnt[j]++;
            }
        }
    }
}

int main()
{
    scanf("%d %d", &n, &m);
    memset(cnt, 0, sizeof(cnt));
    memset(ans, 0, sizeof(ans));
    for(int i = 1; i <= n; i++){
        cntn[i] = Getn(i);
    }
    Prime();
    for(int i = 1; i <= n; i++){
        if(cntn[i] > 2){
            cnt[i] = cntn[i] - cnt[i] - 1;
        }
    }
    for(int i = 1; i <= n; i++){
        ans[cnt[i]]++;
    }
    while(m--){
        int k;
        scanf("%d", &k);
        printf("%d\n", ans[k]);
    }
    return 0;
}

I、牛牛的汉诺塔

因为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 = 1e5 + 10;
const LL mod = 1e9 + 7;

struct node {
    LL data[6];
    node() {
        memset(data,0, sizeof(data));
    }
};

node operator + (node a,node b) {
    node c;
    for (int i = 0;i < 6;i++) {
        c.data[i] = a.data[i] + b.data[i];
    }
    return c;
}

void mov(int x,int y,node &temp) {
    if(x==0&&y==1)++temp.data[0];
    if(x==0&&y==2)++temp.data[1];
    if(x==1&&y==0)++temp.data[2];
    if(x==1&&y==2)++temp.data[3];
    if(x==2&&y==0)++temp.data[4];
    if(x==2&&y==1)++temp.data[5];
}

node dp[3][3][3][105];
bool vis[3][3][3][105];

node hanoi(int a,int b,int c,int n) {
    if (vis[a][b][c][n])
        return dp[a][b][c][n];
    if (n == 1) {
        mov(a,c,dp[a][b][c][n]);
        vis[a][b][c][n] = true;
        return dp[a][b][c][n];
    }
    node temp;
    temp = temp + hanoi(a,c,b,n-1);
    mov(a,c,temp);
    temp = temp + hanoi(b,a,c,n-1);
    vis[a][b][c][n] = true;
    return dp[a][b][c][n] = temp;
}


int main() {
    int n;
    scanf("%d",&n);
    node ans = hanoi(0,1,2,n);
    printf("A->B:%lld\n",ans.data[0]);
    printf("A->C:%lld\n",ans.data[1]);
    printf("B->A:%lld\n",ans.data[2]);
    printf("B->C:%lld\n",ans.data[3]);
    printf("C->A:%lld\n",ans.data[4]);
    printf("C->B:%lld\n",ans.data[5]);
    printf("SUM:%lld\n",(1LL<<n)-1);
    return 0;
}

J、牛牛的宝可梦Go

首先我们用 floyd 求出各个点之间的最短距离,然后我们对宝可梦出现的时间进行排序

dp[i]  代表 到达 i 点获得的最大战斗力

因为之前对宝可梦出现对时间已经进行了排序,所以很容易想到去枚举之前的点(因为肯定是从时间小的转移到时间大的)

但是很可惜 k 的范围是 1e5  ,所以这种O(k^2) 的算法是超时了的

那么我们如何去优化呢?

由于地图的大小只有200,所以200步以后可以转移到任意位置

我们不妨改成枚举步长 这样复杂度就可以降为O(200*k)

#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 = 1e5 + 10;
const LL mod = 1e9 + 7;

struct Node {
    LL t,p,w;
}a[maxn];
bool cmp(Node a, Node b ){
    return a.t<b.t;
}
LL dp[maxn];
LL dis[210][210];


int main() {
    int n,m;
    scanf("%d%d",&n,&m);
    memset(dis,INF, sizeof(dis));
    for (int i = 1;i <= n;i++)
        dis[i][i] = 0;
    for (int i = 1;i <= m;i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        dis[u][v] = dis[v][u] = 1;
    }
    LL k;
    scanf("%lld",&k);
    for (int i = 1;i <= k;i++) {
        scanf("%lld%lld%lld",&a[i].t,&a[i].p,&a[i].w);
    }
    sort(a+1,a+1+k,cmp);
    a[0].t = 0,a[0].p = 1,a[0].w = 0;
    for (int l = 1;l <= n;l++) {
        for (int i = 1;i <= n;i++) {
            for (int j = 1;j <= n;j++)
                dis[i][j] = min(dis[i][j],dis[i][l]+dis[l][j]);
        }
    }
    LL ans = 0;
    for (int i = 1;i <= k;i++)
        dp[i] = -INF;
    for (int i = 1;i <= k;i++) {
        for (int j = 1;j <= 200 && j <= i;j++) {
            if (a[i].t-a[i-j].t >= dis[a[i].p][a[i-j].p] ) {
                dp[i]=max(dp[i],dp[i-j]+a[i].w);
            }
            ans = max(ans,dp[i]);
        }
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

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