CodeCraft-19 and Codeforces Round #537 (Div. 2)

A. Superhero Transformation

水题,注意两个字符串可能长度不相等。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll; 
const int maxn=100010;
map<char ,int >m;
int main(){
    m['a']=m['e']=m['i']=m['o']=m['u']=1;
    string s1,s2;
    while(cin>>s1>>s2)
    {
        int flag=1;
        if(s1.length()!=s2.length())flag=0;
        for(int i=0;i<s1.length();i++)
        {
            if(m[s1[i]]!=m[s2[i]]){
                flag=0;
                break;
            }
        }
        if(flag)puts("Yes");
        else puts("No");
    }
} 
View Code

B. Average Superhero Gang Power

题意:有n个人,每个人都有权值,然后你有m次操作,每次操作可以删掉一个人或者给一个人的权值加一,使得最后剩下的人平均数最大。

坑爹题,后台数据太水了,一开始以为只要删的剩下权值最大的那个一人,然后全加加给这个人就好了,居然过了。

一个半小时后被hack(感谢大哥比赛中hack的我,否则要掉分了),发现比如一组样例如果全是“7 7 7 7 7”这样的就会错,因为删人不一定是最好的选择,所以就枚举删几个人,然后取最大值就好了。

比赛刚结束我的rank1200+,rejudge后变600+,新年惨案哈哈哈。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll; 
const int maxn=100010;
double a[maxn],b[maxn];
int n,m,k;
int main(){
    while(cin>>n>>k>>m)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%lf",&a[i]);
        }
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++)
        {
            b[i]=b[i-1]+a[i];
        }
        double ans=0;
        double tot,po;
        for(int i=0;i<=m&&i<=n-1;i++)
        {
            tot=b[n]-b[i];
            po=n-i;
            if(i<m)
            tot+=min(po*k,(double)m-i);
            ans=max(ans,tot/po);
        }
        printf("%.8f
",ans);
    }
} 
View Code

C. Creative Snap

题意:有2^n个格子,格子可能是空的,也可能站着许多人,有两种操作。操作一是把某一列格子平均分成两部分,操作二是把某一列格子摧毁,如果这一列格子上没有人,那么就需要耗费A的代价,如果格子上有na个人,那么就要耗费B*na*l 的代价,问把所有格子全部摧毁的最小代价是啥。

思路:一开始完全没思路,想了很久,有人说暴力递归可以过,然后就。。试了一下?真的过了,AC后想了一下合法性。

有一个很显然的性质,就是如果一列格子全是空的,那么必定直接付出A的代价进行摧毁,如果有人的话,就要进行考虑。但是发现最多只有k个人,也就是需要考虑的次数不会太多,所以递归并不是nlogn(n=2^30),而是klogk?

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll; 
const int maxn=100010;
int n,k;
ll A,B;
ll a[maxn];
ll dfs(ll l,ll r){
    ll val=upper_bound(a,a+k,r)-lower_bound(a,a+k,l);
    if(val==0) return A;
    if(l==r) return val*B;
    ll mid=(l+r)>>1;
    ll left=dfs(l,mid),right=dfs(mid+1,r);
    return min(left+right,(val)*B*(r-l+1));
}
int main(){
    while(cin>>n>>k>>A>>B)
    {
        for(int i=0;i<k;i++)
        {
            scanf("%lld",&a[i]);
        }
        sort(a,a+k);
        ll ans=dfs(1,(ll)pow(2,n));
        printf("%lld
",ans);
    }
} 
View Code

D. Destroy the Colony

补。

题意:

  给出一列大小写都有的字符串,字符串长度为偶数,每种字符代表一种人,给出q次查询,每次查询,都是讲所有人分成两部分序列,第x个字符代表的人和第y个字符代表的人要在同一个序列里面,问这样的方案数有多少。序列内排列不同则方案不同,两个序列顺序不同则方案不同。

思路:

  设将字符串合法的分成两部分的方案数是 f 。每个字符的数量是ci,n为字符串长度,m=n/2。

  则答案应该是$frac{(m)! *  (m)!  *f *2}{  ci!   *cj !*……    }$

  我们发现这个式子里只有f是不知道的,f等于把x和y放入一个大小为m的背包的方案数。

  如果直接证明做,时间复杂度会超时,所以我们先计算出,所有的n/2大小背包的方案数,如果我们此时把x和y的物品全部从这个背包中减去,就得到了不含x和y的背包的方案数,这个方案数和含xy是一样的。每次减完后再加回去。

  而对于x和y相等的情况下,n/2的背包要么含x,要么不含x,所以此时背包数就等于f*2.

  注意背包如果为空,则不要处理,否则会出错,加减的顺序也要注意。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll; 
const int maxn=100010;
const ll p=1e9+7;
int n,m;
char s[maxn];
ll bur[60];
ll fac[maxn],inv[maxn],ans[100][100],dp[maxn],tep[maxn];
int find(char c){
    if(c>='A'&&c<='Z')return c-'A'+26;
    return c-'a';
}
void init(){
    clr(bur,0),clr(dp,0),clr(inv,0);
}
ll qpow(ll a,ll b){
    a%=p;
    ll res=1;
    while(b){
        if(b&1)res*=a;
        res%=p;
        a*=a,a%=p;
        b>>=1;
    }
    return res;
}
int main(){
    while(scanf("%s",s+1)!=EOF)
    {
        init();
        n=strlen(s+1);
        for(int i=1;i<=n;i++)
        {
            bur[find(s[i])]++;
        }
        fac[0]=1;
        for(int i=1;i<=n;i++)
        {
            fac[i]=i*fac[i-1]%p;
        }
        ll num=fac[n/2]*fac[n/2]%p;
        for(int i=0;i<52;i++)
        {
            if(bur[i]==0)continue;
            inv[i]=qpow(fac[bur[i]],p-2);
            num=num*inv[i]%p;
        }
        // printf("num:%lld
",num);
        dp[0]=1;
        for(int i=0;i<52;i++)
        {
            if(!bur[i])continue;
            for(int j=n/2;j>=bur[i];j--)
            {
                dp[j]+=dp[j-bur[i]];
                dp[j]%=p;
            }
        }
        for(int i=0;i<52;i++)
        {
            ans[i][i]=dp[n/2];
        }
        for(int i=0;i<52;i++)
        {
            if(!bur[i])continue;
            for(int j=0;j<=n/2;j++)tep[j]=dp[j];
            for(int j=bur[i];j<=n/2;j++)
            {
                tep[j]=(tep[j]-tep[j-bur[i]]+p)%p;
            }
            for(int k=i+1;k<52;k++)
            {
                if(!bur[k])continue;
                for(int j=bur[k];j<=n/2;j++)
                {
                    tep[j]=(tep[j]-tep[j-bur[k]]+p)%p;
                }
                ans[i][k]=ans[k][i]=tep[n/2]*2%p;
                for(int j=n/2;j>=bur[k];j--)
                {
                    tep[j]=(tep[j]+tep[j-bur[k]])%p;
                }
                
            }
        }
        cin>>m;
        int x,y;
        while(m--)
        {
            scanf("%d%d",&x,&y);
            x=find(s[x]),y=find(s[y]);
            printf("%lld
",num*ans[x][y]%p);
        }
    }
    
}
View Code

E. Tree

补题。

题意:

  给出一棵树,q次询问,每次询问给出k个树上的点,然后给出r,以r这个节点为根,将这k个点分成m组,要求组内的点,任意两点不能有祖先关系。求方案分配数。

思路:

  参考了官方题解和Dup4的博客,加上了自己的一些理解。

  当我们固定根节点为1的时候,我们设h[ i ]表示已经有几个 i 的祖先节点放入这些集合了。

  $dp[ i ]  [ j ]$ 表示将前 i 个点放入 j 个集合的方案数。

  得到转移方程$dp[ i ] [ j ] =dp[ i -1 ][  j ]*(j - h[ i ])+dp[ i-1 ][ j-1 ]$。

  这个方程的后半部分很好理解,就是把前i-1个点放完后,新开一个背包,把点放进去。

  而前半部分,就是要把这个点放入已经存在的背包里面了,那(j-h[ i ] )是什么意思呢,因为我们能把当前节点放入所有没有他祖先节点的集合里,而他的祖先节点必定也不是在同一个组里的,所以方案数就是这个。并且只要我们将h[ i ]从小到大排序,然后按序插入,这样就可以保证所有祖先节点都比子节点先进行dp。

  那怎么处理这个h[ i ] 呢,还是对于固定1为根节点的来说,如果我们先处理好dfs序,然后把每个节点的子树节点用BIT全部加1,那么对于i这个点的值,就是i的祖先节点的值加上本身了。

  现在考虑怎么把固定根为1的树推广向普通树,关键还是这个h[ i ],那给出r,我们可以先求出r这个点的祖先节点数量,在求出每个x节点和r的lca,记做y,求出y的祖先节点个数,然后图上画一画就知道怎么转换了。

  求lca这里用了树链剖分。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll; 
const int maxn=100010;
const ll p=1e9+7;
int n,q,dep[maxn],son[maxn],siz[maxn],lp[maxn],rp[maxn],top[maxn],fa[maxn];
int cnt;
vector<int >ve[maxn];
int k,m,r,vec[maxn],a[maxn];
ll dp[maxn],h[maxn];
bool vis[maxn];
void init(){
    for(int i=1;i<=n;i++)ve[i].clear();
    cnt=0;
    clr(a,0);
}
void dfs(int u,int f){
    siz[u]=1;
    son[u]=0;
    fa[u]=f;
    for(int i=0;i<ve[u].size();i++)
    {
        int v=ve[u][i];
        if(v==f)continue;
        dep[v]=dep[u]+1;
        dfs(v,u);
        siz[u]+=siz[v];
        if(son[u]==0||siz[v]>siz[son[u]]){
            son[u]=v;
        }
    }
}
void gettop(int u,int to){
    lp[u]=++cnt;
    top[u]=to;
    if(son[u]==0){
        rp[u]=cnt;
        return;
    }
    gettop(son[u],to);
    for(auto v:ve[u]){
        if(v != fa[u] &&v!=son[u]){
            gettop(v,v);
        }
    }
    rp[u]=cnt;
}
int lca(int u,int v){
    while(top[u]!=top[v]){
        if(dep[top[u]] < dep[top[v]])swap(u,v);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v])swap(u,v);
    return u;
}
int lowbit(int x){
    return x & (-x);
}
void add(int x,int val){
    while(x<=n){
        a[x]+=val;
        x+=lowbit(x);
    }
}
void update(int l,int r,int val){
    add(l,val),add(r+1,-val);
}
int getsum(int x){
    int res=0;
    while(x>0){
        res+=a[x];
        x-=lowbit(x);
    }
    return res;
}
int main(){
    while(cin>>n>>q)
    {
        init();
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            ve[u].push_back(v);
            ve[v].push_back(u);
        }
        dfs(1,0);
        gettop(1,1);
        while(q--)
        {
            
            scanf("%d%d%d",&k,&m,&r);
            for(int i=1;i<=m;i++)dp[i]=0;
            dp[0]=1;
            for(int i=1;i<=k;i++)
            {
                scanf("%d",&vec[i]);
                vis[vec[i]]=1;
                update(lp[vec[i]],rp[vec[i]],1);
            }
            int ancnum=getsum(lp[r]);
            for(int i=1;i<=k;i++)
            {
                int x=vec[i];
                int y=lca(x,r);
                h[x]=getsum(lp[x])+ancnum-2*getsum(lp[y])+(vis[y])-1;
            }
            for(int i=1;i<=k;i++)
            {
                vis[vec[i]]=0;
                update(lp[vec[i]],rp[vec[i]],-1);
            }
            sort(vec+1,vec+1+k,[](int x,int y){
                return h[x]<h[y];
            });
            if(h[vec[k]]>=m)puts("0");
            else{
                
                for(int i=1;i<=k;i++)
                {
                    int x=vec[i];
                    for(int j=min(i,m);j>=0;j--)
                    {
                        if(j<=h[x])dp[j]=0;
                        else{
                            dp[j]=(dp[j]*(j-h[x]))%p+dp[j-1];
                        }
                    }
                }
                ll ans=0;
                for(int i=1;i<=m;i++)ans=(ans+dp[i])%p;
                printf("%lld
",ans);
                
            }
        }
    }
}
View Code
原文地址:https://www.cnblogs.com/mountaink/p/10351663.html