Codeforces 每日一练 855C+1172A+1151C

855C Helga Hufflepuff’s Cup

传送门
题意:对一棵树上的n个点染色,有m种颜色,对于颜色k,最多染x个,并且每一个与颜色为k的节点相连的点的颜色必须小于k
很明显是一道树形dp的题啦,一开始只开了二维,记录取k/不取k,发现转移不动,然后意识到每个节点有三种情况:取k,小于k,大于k,并且我们还要记录当前有几个节点颜色为k,并且已经限定了x≤10,自然开三维去转移就好,比如dp[i][j][l]就代表第i个节点染色情况为j时有l个节点颜色为k的情况数。
但是考虑到每加入一棵子树时应该是与当前答案相乘的关系,所以在转移时要记录每次加入的答案,然后相乘处理。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 100005
#define il inline
#define mod 1000000007
vector<int> g[maxn];
ll dp[maxn][3][15];
int n,num;
ll m,k;
void dfs(int x,int fa){
    dp[x][0][0]=k-1;
    dp[x][2][0]=m-k;
    dp[x][1][1]=1;
    for(auto i:g[x]){
         if(i!=fa){
             dfs(i,x);
             ll tmp[15][3]={0};
             for (int j = 0; j <=num ; ++j) {
                 for (int l = 0; l <=j ; ++l) {
                     tmp[j][0]+=(dp[i][0][l]+dp[i][1][l]+dp[i][2][l])*dp[x][0][j-l];
                     tmp[j][0]%=mod;
                     tmp[j][1]+=dp[i][0][l]*dp[x][1][j-l];
                     tmp[j][1]%=mod;
                     tmp[j][2]+=(dp[i][2][l]+dp[i][0][l])*dp[x][2][j-l];
                     tmp[j][2]%=mod;
                 }
             }
             for (int j = 0; j <=num ; ++j) {
                 dp[x][0][j]=tmp[j][0];
                 dp[x][1][j]=tmp[j][1];
                 dp[x][2][j]=tmp[j][2];
             }
         }
    }
}
int main() {
    int x;
    cin>>n>>m;
    for (int i = 0; i <n-1 ; ++i) {
        int u,v;
        cin>>u>>v;
        g[v].push_back(u);
        g[u].push_back(v);
    }
    cin>>k>>x;
    num=x;
    dfs(1,0);
    ll ans=0;
    for (int i = 0; i <=x ; ++i) {
        ans+=dp[1][2][i]+dp[1][0][i]+dp[1][1][i];
        ans%=mod;
    }
    cout<<ans%mod;
    return 0;
}

(貌似HDU也有一道类似思路的题)

1172A Nauuo and Cards

传送门
题意:你手里有n张牌,场上有n张牌,每张牌的值在0~n,每次可以从手中选择一张牌放在场上牌堆的最后,并且把第一张取走,询问最少多少次可以使得场上的顺序为1-n。
很明显答案是有上界的,最差就是把n张都取走,然后依次放置,而对于初始每一张牌,假设其值为i,位置为p[i],那么将其移动到正确位置需要p[i]-i+n+1次,显然取max(p[i]-i+n+1)次就可以完成目标。
但是仍然存在存在更优方案的情况,不如考虑1 2 3,如果我们按照上面的方法,是4,但是显然答案是0。很容易联想到如果我们可以1,2,3……n的放置,那么我们就没必要用上面的取法,而这种情况需要从场上的末尾开始向前存在i ,i-1,i-2,……1,并且我们每次可以把末尾牌面+1的牌放到末尾,此时需要的次数是小于上面的情况的。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 200005
#define mod 1000000007
#define rep(i,m,n) for(int (i)=(m);(i)<=(n);(i)++)
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int a[maxn],b[maxn];
int pos[maxn];
int main() {
    IOS
    int n;
    cin>>n;
    int ans=0;
    rep(i,1,n)cin>>a[i];
    rep(i,1,n){cin>>b[i];pos[b[i]]=i;}
    if(pos[1]!=0){
        int i;
        for (i = 2; pos[i]-pos[1]==i-1 ; ++i){}
        if(pos[i-1]==n){
            int j;
            for (j = i; j<=n&&pos[j]<=j-i ; ++j){}
            if(j>n){
                cout<<n-i+1;
                return 0;
            }
        }
    }
    rep(i,1,n)ans=max(ans,pos[i]-i+n+1);
    cout<<ans;
    return 0;
}

1151C Problem for Nazar

传送门
题意:每次从奇数列和偶数列取2的幂次个数写到黑板上,比如1 | 2 4 | 3 5 7 9 | 6 8 10……,询问区间[l,r]的和。
上午刚学了数位dp,直接就想到了前缀和相减的思路。对于前i个,虽然看起来计算挺麻烦,但是不难发现

  • 奇数列中,前i项和为i*i
  • 偶数列中,前i项和为i*(i+1)

那么我们只需要去找前i个数中有多少奇数和多少偶数就好了。
(日常瞎**取模wa了半天)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define maxn 100005
#define mod 1000000007
ll calc(ll x){
    int cnt=1;
    ll l=1,lr=0ll,r=0ll;
    while(x>=l){
        int fl=0;
        if(cnt==1&&x>=l){
            fl=1;
            x-=l;
            lr+=l;
            l*=2;
        }else if(cnt==0&&x>=l){
            fl=1;
            x-=l;
            r+=l;
            l*=2;
        }
        cnt++;
        cnt%=2;
        if(!fl)break;
    }
    if(!cnt)r+=x;
    else lr+=x;
    lr%=mod;
    r%=mod;
    return ((lr*lr)%mod+(r*(r+1))%mod)%mod;
}
int main() {
    ll l,r;
    cin>>l>>r;
    cout<<((calc(r)-calc(l-1))%mod+mod)%mod;
    return 0;
}
posted @ 2020-04-17 21:56  Bazoka13  阅读(111)  评论(0编辑  收藏  举报