• 2020牛客寒假算法基础集训营3(全解)


    题目链接:https://ac.nowcoder.com/acm/contest/3004

    这里出题人的题解很详细,有多种解法:https://ac.nowcoder.com/discuss/365306?type=101&order=0&pos=1&page=1

    题目说明:

    A.牛牛的DRB迷宫I      B.牛牛的DRB迷宫II       C.牛牛的数组越位      D.  牛牛与二叉树的数组存储

    (DP|记忆化搜索)    (二进制)                       (模拟+STL)           (模拟)

    E.牛牛的随机数          F.牛牛的Link Power I     G.牛牛的Link Power II    H.牛牛的k合因子数

    (数位DP)               (前缀和)                       (线段树)                       (素数筛)

    I.牛牛的汉诺塔          J.牛牛的宝可梦Go

    (记忆化搜索)       (DP+最短路)

    A.牛牛的DRB迷宫I 

    题目大意:给你n*m的图(n,m<=50),问你从(1,1)到(n,m)有多少种走法,其中地图包含B,R,D(R只能向右走,D只能向下走,B都可),答案对1e9+7取模

    示例

    输入

    5 5
    RBBBR
    BBBBB
    BBBDB
    BDBBB
    RBBBB

    输出

    25

    。。。一道格子DP,我们应该都做过只能向下和向右走的地图,$dp[i][j]+=dp[i][j-1]+dp[i-1][j]$,那么这题其实是一样的,不过我们也可以搜索,只不过为了防止TLE,我们可以记忆化一下。

    DP的AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    const int mac=55;
    const int mod=1e9+7;
    int n,m;
    char s[mac][mac];
    ll dp[mac][mac];
    
    int main(int argc, char const *argv[])
    {
        int n,m;
        scanf ("%d%d",&n,&m);
        for (int i=1; i<=n; i++)
            scanf ("%s",s[i]+1);
        dp[n][m]=1;
        for (int i=n; i>=1; i--)
            for (int j=m; j>=1; j--){
                if (s[i][j]=='R') dp[i][j]=(dp[i][j]+dp[i][j+1])%mod;
                else if (s[i][j]=='D') dp[i][j]=(dp[i][j]+dp[i+1][j])%mod;
                else dp[i][j]=(dp[i][j]+dp[i][j+1]+dp[i+1][j])%mod;
            }
        printf("%lld
    ",dp[1][1]);
        return 0;
    }
    View Code

    记忆化搜索的AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int mac=55;
    const int mod=1e9+7;
    #define ok(x,y,n,m) ((x>=1) && (x<=n) && (y>=1) && (y<=m))
    
    char s[mac][mac];
    ll dp[mac][mac],mk[mac][mac];
    int vis[mac][mac],n,m;
    int dx[]={0,1},dy[]={1,0};//向右,向下
    
    ll dfs(int x,int y,int id)
    {
        if (dp[x][y]) return dp[x][y];
        if (x==n && y==m) {dp[x][y]++; return dp[x][y];}
        int xx,yy;
        if (id==0 || id==1){
            xx=x+dx[id];yy=y+dy[id];
            if (vis[xx][yy] || !ok(xx,yy,n,m)) return 0;
            vis[xx][yy]=1;
            dp[x][y]=(dp[x][y]+dfs(xx,yy,mk[xx][yy]))%mod;
            vis[xx][yy]=0;
        } 
        else {
            for (int i=0; i<2; i++){
                xx=x+dx[i];yy=y+dy[i];
                if (vis[xx][yy] || !ok(xx,yy,n,m)) continue;
                vis[xx][yy]=1;
                dp[x][y]=(dp[x][y]+dfs(xx,yy,mk[xx][yy]))%mod;
                vis[xx][yy]=0;
            }
        }
        return dp[x][y];
    }
    
    int main(int argc, char const *argv[])
    {
        scanf ("%d%d",&n,&m);
        for (int i=1; i<=n; i++)
            scanf("%s",s[i]+1);
        for (int i=1; i<=n; i++)
            for (int j=1; j<=m; j++)
                if (s[i][j]=='R') mk[i][j]=0;
                else if (s[i][j]=='D') mk[i][j]=1;
                else mk[i][j]=2;
        memset(dp,0,sizeof dp);
        printf("%lld
    ",dfs(1,1,mk[1][1]));
        return 0;
    }
    View Code

    B.牛牛的DRB迷宫II

    题目大意:和A题题面反过来的,那种地图如果有n种走法,输出地图(大小在50*50)

    示例

    输入

    25

    输出

    5 5
    RBBBR
    BBBBB
    BBBDB
    BDBBB
    RBBBB

    emmmm,这张地图其实可以表示任何一个数,两种走法和一种走法,这样可以安排组合也就是几个两种走法的,几个一种走法的,这样一定能构成n。

    #include <bits/stdc++.h>
    using namespace std;
    
    char mp[55][55];
    
    int main(int argc, char const *argv[])
    {
        int n,m,k;
        n=32,m=30;
        scanf ("%d",&k);
        for (int i=1; i<=m; i++) mp[n][i]='R';
        for (int i=1; i<n; i++){//对角线的前一个到后一个先设为B,其余废弃
            for (int j=1; j<=m; j++){
                if (i==1) mp[1][j]=(j<=2)?'B':'R';
                else {
                    if (j<i-1) mp[i][j]='D';
                    else if (j<i+2) mp[i][j]='B';
                    else mp[i][j]='R';
                }
            }
        }//此时mp[n][m]为2^(n-1)
        for (int i=1; i<=m; i++){//将k拆解为二进制数
            if (!(k&(1<<(i-1)))) mp[i+1][i]='R';//如果k在此位为0,那么就变B为R
        }
        printf("%d %d
    ",n,m);
        for (int i=1; i<=n; i++)
            printf("%s
    ",mp[i]+1);
        return 0;
    }
    View Code

    C.牛牛的数组越位

    题目大意:T组数据,给你一个二维数组[n][m](n*m<=2e7)。接下来p个操作,每次操作$x,y,val$如果$x,y$有越界行为,则输出整个数组,之后输出“Undefined Behaviour”,如果有非法行为,直接输出“Runtime error”,否则输出整个数组后输出“Accepted”。

    示例

    输入

    4
    2 4 8
    -1 4 1
    1 -3 2
    2 -6 3
    0 3 4
    -1 8 5
    -2 13 6
    -100 406 7
    100 -393 8
    5 5 1
    -1 8 1234
    10 10 1
    5 -51 1
    1 1 1
    0 0 7

    输出

    1 2 3 4
    5 6 7 8
    Undefined Behaviour
    0 0 0 1234 0
    0 0 0 0 0
    0 0 0 0 0
    0 0 0 0 0
    0 0 0 0 0
    Undefined Behaviour
    Runtime error
    7
    Accepted

    就是个纯粹的模拟题,只不过如何判断二维数组的大小不知道,而且清空太麻烦了,我们可以用map,这样就免去了清空数组的时间:

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    #define ok(x,y,n,m) ((x>=0) && (x<n) && (y>=0) && (y<m))
    
    int main(int argc, char const *argv[])
    {
        int t;
        scanf ("%d",&t);
        while (t--){
            int n,m,pp;
            scanf ("%d%d%d",&n,&m,&pp);
            int re=0,ub=0;
            typedef pair<int,int>use;
            map<use,int>q;
            int lim=n*m;
            while (pp--){
                int x,y,val;
                scanf ("%d%d%d",&x,&y,&val);
                int use=m*x+y;
                if (use<0 || use>=lim) re=1;//注意有等于号
                if (!ok(x,y,n,m)) ub=1;
                int xx=use/m,yy=use%m;
                q[make_pair(xx,yy)]=val;
            }
            if (re) printf("Runtime error
    ");
            else if (ub) {
                for (int i=0; i<n; i++)
                    for (int j=0; j<m; j++){
                        printf("%d",q[make_pair(i,j)]);
                        printf("%c",j==(m-1)?'
    ':' ');
                    }
                printf("Undefined Behaviour
    ");
            }
            else {
                for (int i=0; i<n; i++)
                    for (int j=0; j<m; j++){
                        printf("%d",q[make_pair(i,j)]);
                        printf("%c",j==(m-1)?'
    ':' ');
                    }
                printf("Accepted
    ");
            }
        }
        return 0;
    }
    View Code

    D.牛牛与二叉树的数组存储

    题目大意:给你一颗满二叉树(数组表示)(不存在的节点用-1代替)输出树的尺寸和根节点,并按照顺序打印每个节点的父亲节点、左孩子、右孩子

    示例

    输入

    7
    3 -1 2 -1 -1 1 4

    输出

    The size of the tree is 4
    Node 3 is the root node of the tree
    The father of node 1 is 2, the left child is -1, and the right child is -1
    The father of node 2 is 3, the left child is 1, and the right child is 4
    The father of node 3 is -1, the left child is -1, and the right child is 2
    The father of node 4 is 2, the left child is -1, and the right child is -1

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
     
    const int mac=1e5+10;
     
    struct node
    {
        int fa,l,r;
    }ans[mac];
    int a[mac],n;
     
    int sonval(int rt)
    {
        if (rt>n) return -1;
        return a[rt];
    }
     
    void build(int rt,int lim,int fa)
    {
        if (rt>lim) return ;
        if (a[rt]==-1) return ;
        ans[a[rt]]=node{fa,sonval(rt<<1),sonval(rt<<1|1)};
        build(rt<<1,lim,a[rt]);build(rt<<1|1,lim,a[rt]);
    }
     
    int main(int argc, char const *argv[])
    {
        int sz=0;
        scanf ("%d",&n);
        for (int i=1; i<=n; i++){
            scanf("%d",&a[i]);
            if (a[i]!=-1) sz++;
            ans[i].fa=ans[i].l=ans[i].r=-1;
        }
        build(1,n,-1);
        printf("The size of the tree is %d
    ",sz);
        printf("Node %d is the root node of the tree
    ",a[1]);
        for (int i=1; i<=sz; i++){
            printf("The father of node %d is %d, ",i,ans[i].fa);
            printf("the left child is %d, ",ans[i].l);
            printf("and the right child is %d
    ",ans[i].r);
        }
        return 0;
    }
    View Code

    E.牛牛的随机数  

    题目大意:T组数据(<=1e5),从区间$[l_{1},r_{1}]$和$l_{2},r_{2}$($l_{1},r_{1},l_{2},r_{2}<=1e18$)中分别取一个数$a,b$,问你$aigoplus b$的数学期望,并对1e9+7取模

    示例

    输入

    2
    3 5 7 8
    1 3 3 5

    输出

    500000011
    222222228

    没办法,先看看暴力吧,$frac{sum_{x=l_{1}}^{r_{1}}sum_{y=l_{2}}^{r_{2}}xigoplus y}{(r_{1}-l_{1}+1) imes (r_{2}-l_{2}+1)}$

    然后就是跑数位DP了,出题人说的明白了,我就不多说了,一些小细节打了注释

    以下是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const ll mod=1e9+7;
    ll a[105],b[105],na[105],nb[105];
    struct node
    {
        ll s,ss;
    };
    
    node dp[105];
    bool vis[105];
    
    node dfs(int pos,bool limt1,bool limt2)//x,y的第pos位限制
    {
        if(pos==-1) return node{0,1};
        if(!limt1 && !limt2 && vis[pos]) return dp[pos];//pos位如果没有限制
        if(!limt1 && limt2)//y没有限制
           return node{(((1ll<<pos)%mod)*((2ll<<pos)%mod-1)%mod)*((nb[pos]+1)%mod)%mod,(((nb[pos]+1)%mod)*((2ll<<pos)%mod))%mod};
        if(limt1 && !limt2)
           return node{(((1ll<<pos)%mod)*((2ll<<pos)%mod-1)%mod)*((na[pos]+1)%mod)%mod,(((na[pos]+1)%mod)*((2ll<<pos)%mod))%mod};
        int up1=limt1?a[pos]:1;//如果没有限制,那你们此位的二进制数位最大1
        int up2=limt2?b[pos]:1;
        node temp{0,0};
        for (int i=0; i<=up1; i++)
            for (int j=0; j<=up2; j++){
                node sm=dfs(pos-1,limt1 && i==a[pos],limt2 && j==b[pos]);
                if (i^j){
                    temp.s=(temp.s+(1ll<<pos)%mod*sm.ss%mod+sm.s)%mod;
                    temp.ss=(temp.ss+sm.ss)%mod;
                }
                else{
                    temp.s=(temp.s+sm.s)%mod;
                    temp.ss=(temp.ss+sm.ss)%mod;
                }
            }
        if(!limt1 && !limt2){
            vis[pos]=true;
            dp[pos]=temp;
        }
        return temp;
    }
    ll solve(long long x,long long y)
    {
        memset(na,0,sizeof(na));
        memset(nb,0,sizeof(nb));
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        int len1=0,len2=0;
        while(x){//二进制下的每一位
            a[len1++]=x&1;
            x>>=1;
        }
        while(y){
            b[len2++]=y&1;
            y>>=1;
        }
        int len=max(len1,len2)-1;
        for(int i=0;i<=len;++i){
            x+=a[i]<<i;
            na[i]=x;
            y+=b[i]<<i;
            nb[i]=y;
        }
        return dfs(len,true,true).s;
    }
    ll qick(ll x,ll y)
    {
        ll ans=1;
        while(y){
            if(y&1) ans=(x*ans)%mod;
            x=(x*x)%mod;
            y>>=1;
        }
        return ans;
    }
    int main()
    {
        int t;
        memset(vis,false,sizeof(vis));
        scanf("%d",&t);
        while(t--){
            ll l1,l2,r1,r2;
            scanf("%lld%lld%lld%lld",&l1,&r1,&l2,&r2);
            ll ans=0;
            ans+=solve(r1,r2);
            ans-=solve(r1,l2-1);
            ans-=solve(l1-1,r2);
            ans+=solve(l1-1,l2-1);
            ans=(ans%mod+mod)%mod;
            ans=ans*qick(((r1-l1+1)%mod)*((r2-l2+1)%mod)%mod,mod-2)%mod;
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code

    F. 牛牛的Link Power I 

    题目大意:给你一个01串,dis(u,v)为数组上u到v的距离,(u,v)和(v,u)被认为是同一对。求所有字符1的dis的和,答案对1e9+7取模

    示例

    输入

    3
    101

    输出

    2

    记录一下1的个数和每个1的位置,然后记录一下所有1位置的和,第一位置对答案的贡献就是:$(a_{2}-a_{1})+(a_{3}-a_{1})+cdots +(a_{n}-a_{1})=s_{n}-a_{1}-(n-1)*a_{1}$

    由于是无序对,所以,每次s都会减去当前的位置值。

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
     
    const int mac=1e5+10;
    const int mod=1e9+7;
    typedef long long ll;
     
    char s[mac];
    vector<int>g;
     
    int main(int argc, char const *argv[])
    {
        int n;
        scanf ("%d",&n);
        scanf ("%s",s+1);
        int nb=0;
        for (int i=1; i<=n; i++)
            if (s[i]=='1') g.push_back(i);
        ll sum=0;
        for (auto x:g) sum+=x;
        ll ans=0;
        int len=g.size();
        for (auto x:g){
            ans=(ans+(sum-x)%mod-1ll*x*(len-1)%mod+mod)%mod;
            sum-=x;len--;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

    G.牛牛的Link Power II    

    题目大意:和上面的I差不多,只不过多了个修改操作,q,pos,q=1即将pos位置的0改成1,q=2,将pos位置1改为0

    示例

    输入

    5
    00001
    7
    1 1
    2 5
    2 1
    1 2
    1 4
    1 3
    1 1

    输出

    0
    4
    0
    0
    0
    2
    4
    10

    和上面差不多的思路,维护一下pos前面和后面的数量(num)和位置和(sum),那么新加一个点对前面的贡献也就是$ans+pos*num-sum$,对后面的贡献就是$ans+sum-pos*num$,这个可以用线段树维护

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    const int mac=1e5+10;
    const int mod=1e9+7;
    typedef long long ll;
    
    struct node
    {
        ll nb,val;
    }tree[mac<<2];
    char s[mac];
    
    void build(int l,int r,int rt)
    {
        tree[rt].nb=tree[rt].val=0;
        if (l==r) return;
        int mid=(l+r)>>1;
        build(lson);build(rson);
    }
    
    void update(int l,int r,int rt,int pos,int mk,int val)
    {
        if (l==r) {
            tree[rt].nb=mk; 
            tree[rt].val=val;
            return;
        }
        int mid=(l+r)>>1;
        if (pos<=mid) update(lson,pos,mk,val);
        else update(rson,pos,mk,val);
        tree[rt].nb=tree[rt<<1].nb+tree[rt<<1|1].nb;
        tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;
    }
    
    node solve(node a,node b)
    {
        return node{a.nb+b.nb,a.val+b.val};
    }
    
    node query(int l,int r,int rt,int L,int R)
    {
        node ans={0,0};
        if (l>=L && r<=R) return tree[rt];
        int mid=(l+r)>>1;
        if (L<=mid) ans=solve(ans,query(lson,L,R));
        if (R>mid) ans=solve(ans,query(rson,L,R));
        return ans;
    }
    
    int main(int argc, char const *argv[])
    {
        int n;
        scanf ("%d",&n);
        build(1,n,1);
        scanf ("%s",s+1);
        ll sum=0;
        for (int i=1; i<=n; i++)
            if (s[i]=='1') update(1,n,1,i,1,i);
        int m;
        scanf ("%d",&m);
        for (int i=1; i<=n; i++){
            if (s[i]=='1'){
                node p=query(1,n,1,i+1,n);
                sum=(sum+p.val-p.nb*i%mod+mod)%mod;
            }
        }
        printf("%lld
    ",sum);
        while (m--){
            int q,pos;
            scanf ("%d%d",&q,&pos);
            if (q==1){
                node p=query(1,n,1,1,pos);
                sum=(sum+(pos*p.nb%mod)-p.val+mod)%mod;
                p=query(1,n,1,pos,n);
                sum=(sum+p.val-(pos*p.nb%mod)+mod)%mod;
                update(1,n,1,pos,1,pos);
            }
            else {
                node p;
                if (pos!=1){
                    p=query(1,n,1,1,pos-1);
                    sum=(sum-(((pos*p.nb%mod)-p.val+mod)%mod)+mod)%mod;
                } 
                if (pos!=n){
                    p=query(1,n,1,pos+1,n);
                    sum=(sum-((p.val-(pos*p.nb%mod)+mod)%mod)+mod)%mod;
                }
                update(1,n,1,pos,0,0);
            }
            printf("%lld
    ",sum);
        }
        return 0;
    }
    View Code

    H.牛牛的k合因子数

    题目大意:输出1~n(1e5)中给定k的情况下k合因子数(有k个因子为合数)的数目。m次询问

    示例

    输入

    10 5
    1
    2
    3
    4
    5

    输出

    4
    1
    0
    0
    0

    埃式筛筛出质数,再对1到n中的所有数用$sqrt{n}$的复杂度求每个数的因子并判断是否为质数就好了。

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
     
    const int mac=1e5+10;
     
    int disprime[mac],vis[mac],ans[mac];
     
    int solve(int x)//判断有几个合数因子
    {
        int ans=0;
        int p=sqrt(x);
        if (p*p==x)
            if (vis[p]) ans++;
        for (int i=1; i<=p; i++){
            if (i*i==x) continue;
            if ((x%i)==0) {
                int u=i,v=x/i;
                if (vis[u]) ans++;
                if (vis[v]) ans++;
            }
        }
        return ans;
    }
     
    int main(int argc, char const *argv[])
    {
        int n,m;
        scanf ("%d%d",&n,&m);
        int p=sqrt(n);
        for (int i=2; i<=p; i++)
            if (!vis[i])
                for (int j=i*i; j<=n; j+=i)
                    vis[j]=1;
        for (int i=2; i<=n; i++){
            disprime[i]=solve(i);
            ans[disprime[i]]++;
        }
        while (m--){
            int x;
            scanf("%d",&x);
            printf("%d
    ",ans[x]);
        }
        return 0;
    }
    View Code

    I.牛牛的汉诺塔 

    题目大意:给你n个盘子,输出移动次数(A-B,A-C,B-A,B-C,C-A,C-B)以及所有的次数:

    示例

    输入

    3

    输出

    A->B:1
    A->C:3
    B->A:1
    B->C:1
    C->A:0
    C->B:1
    SUM:7  

    方法一:打表找规律。。。。嘿嘿嘿,这个公式可不好推,不建议,不过它跑得非常快。。。时间复杂度O(1)

    规律:

    A->B:0,1,1,4,4,15,15,58,58

    A->C:1,1,3,3,9,9,31,31,117,117

    B->A:0,0,1,1,6,6,27,27,

    B->C:0,1,1,4,4,15,15,58,58

    C->A:0,2,2,12,12,54,54,224,224

    C->B:0,0,1,1,6,6,27,27

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
     
    typedef long long ll;
     
    ll qick(ll a,ll b)
    {
        ll ans=1;
        while (b){
            if (b&1) ans*=a;
            a*=a;
            b>>=1;
        }
        return ans;
    }
     
    int main(int argc, char const *argv[])
    {
        int n;
        scanf ("%d",&n);
        ll sum=1ll<<n;
        sum--;
     
        printf("A->B:");
        ll nb=(n-1)/2;
        if ((n-1)%2==0) nb--;
        if (n==1) printf("0
    ");
        else printf("%lld
    ",(3*nb+1+qick(2,2*nb+3))/9);
     
        printf("A->C:");
        nb=n/2;
        if (n%2==0) nb--;
        printf("%lld
    ",(qick(4,nb+1)+6*nb+5)/9);
     
        printf("B->A:");
        nb=n/2;
        if (n%2==0) nb--;
        printf("%lld
    ",(qick(4,nb+1)-3*nb-4)/9);
     
        printf("B->C:");
        nb=(n-1)/2;
        if ((n-1)%2==0) nb--;
        if (n==1) printf("0
    ");
        else printf("%lld
    ",(3*nb+1+qick(2,2*nb+3))/9);
     
        printf("C->A:");
        nb=(n-1)/2;
        if ((n-1)%2==0) nb--;
        printf("%lld
    ",2*(qick(4,nb+1)-3*nb-4)/9);
     
        printf("C->B:");
        nb=n/2;
        if (n%2==0) nb--;
        printf("%lld
    ",(qick(4,nb+1)-3*nb-4)/9);
        printf("SUM:%lld
    ",sum);
        return 0;
    }
    View Code

    方法二:暴搜加个记忆化就完事了

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    
    struct node
    {
        ll val[6];
        node(){memset(val,0,sizeof val);}
    };
    node dp[4][4][4][100];
    int vis[4][4][4][100];
    
    void solve(int x,int y,node &a)
    {
        if(x==0&&y==1) a.val[0]++;
        else if(x==0&&y==2) a.val[1]++;
        else if(x==1&&y==0) a.val[2]++;
        else if(x==1&&y==2) a.val[3]++;
        else if(x==2&&y==0) a.val[4]++;
        else if(x==2&&y==1) a.val[5]++;
    }
    
    node deal(node a,node b)
    {
        node ans;
        for (int i=0; i<6; i++)
            ans.val[i]=a.val[i]+b.val[i];
        return ans;
    }
    
    node dfs(int n,int a,int b,int c)
    {
        if (vis[a][b][c][n]) return dp[a][b][c][n];
        if (n==1){
            solve(a,c,dp[a][b][c][n]);
            vis[a][b][c][n]=1;
            return dp[a][b][c][n];
        }
        node tmp;
        tmp=deal(tmp,dfs(n-1,a,c,b));
        solve(a,c,tmp);
        tmp=deal(tmp,dfs(n-1,b,a,c));
        vis[a][b][c][n]=1;
        return dp[a][b][c][n]=tmp;
    }
    
    int main(int argc, char const *argv[])
    {
        int n;
        scanf("%d",&n);
        node ans=dfs(n,0,1,2);
        printf("A->B:%lld
    ",ans.val[0]);
        printf("A->C:%lld
    ",ans.val[1]);
        printf("B->A:%lld
    ",ans.val[2]);
        printf("B->C:%lld
    ",ans.val[3]);
        printf("C->A:%lld
    ",ans.val[4]);
        printf("C->B:%lld
    ",ans.val[5]);
        printf("SUM:%lld
    ",(1ll<<n)-1);
        return 0;
    }
    View Code

    J.牛牛的宝可梦Go

    题目大意:n个城市(<=200),m条公路(<=10000)每条长度为1,有k个宠物,(<=1e5)将会在时间$t$,地点$pos$出现,其攻击力为$val$请问他能得到的最大攻击为多少?

    示例

    输入

    3 2
    1 2
    2 3
    3
    1 1 5
    2 3 10
    3 2 1

    输出

       11

            我们可以先考虑暴力,枚举每一个宠物,那么第i个宠物肯定是由前i-1个宠物转移过来的,也就是说$dp[i]=max(dp[i],dp[j]+val)$那么复杂度就是$O(K^{2})$然后就直接爆表了。但是我们可以知道的是只有200个城市,那么也就是说一次最多往前拓展200个,那么200个之后的我们直接用前缀最大值维护就好了。

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    const int mac=1e5+10;
    typedef long long ll;
    const ll inf=1e18;
    
    ll dis[250][250];
    ll dp[mac];
    struct node
    {
        int t,pos;
        ll val;
        bool operator <(const node &a)const{
            return t<a.t;
        }
    }a[mac];
    
    int main(int argc, char const *argv[])
    {
        int n,m;
        scanf ("%d%d",&n,&m);
        memset(dis,0x3f,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;
        }
        int q;
        scanf("%d",&q);
        for (int i=1; i<=q; i++){
            int t,p,val;
            scanf("%d%d%d",&t,&p,&val);
            a[i]=node{t,p,val};
        }
        for (int k=1; k<=n; k++)
            for (int i=1; i<=n; i++)
                for (int j=1; j<=n; j++)
                    dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
        sort(a+1,a+1+q);
        for (int i=1; i<=q; i++) dp[i]=-inf;
        a[0].t=0;a[0].pos=1;
        ll tmp=0;
        ll ans=0;
        for (int i=1; i<=q; i++){
            if (i>200) {
                tmp=max(tmp,dp[i-200]);
                dp[i]=a[i].val+tmp;
            }
            for (int j=1; j<=min(i,200); j++){//[i-200,i]
                if (a[i].t-a[i-j].t>=dis[a[i].pos][a[i-j].pos])
                    dp[i]=max(dp[i],dp[i-j]+a[i].val);
            }
            ans=max(ans,dp[i]);
        }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code
    路漫漫兮
  • 相关阅读:
    洛谷P1357 Solution
    洛谷P3469 Solution
    洛谷P2617 Solution
    CF818F Solution
    CF802K Solution
    CF519E Solution
    在代码中改变log的级别
    Java非对称加密解密
    mvn test 远程调试
    rsyn实现服务器源码同步
  • 原文地址:https://www.cnblogs.com/lonely-wind-/p/12285185.html
Copyright © 2020-2023  润新知