牛客练习赛60

A.大吉大利

考虑每一位对答案的贡献。

如果两个数的第位都是,那么就会对答案产生的贡献。

所以答案就是就是第位是的个数。

#include<bits/stdc++.h>
using namespace std;
#define next Next
#define gc getchar
#define int long long
const int N=1e6+5;
int n,m,k,ans,a[N];
char s[N];
//char buf[1<<21],*p1=buf,*p2=buf;
//inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline int read()
{
    int ret=0,f=0;char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){ret=ret*10+c-48;c=gc();}
    if(f)return -ret;return ret;
}
signed main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        int x=read();
        for(int j=0;j<=30;j++)
            if(x&(1<<j))a[j]++;
    }
    for(int i=0;i<=30;i++)ans+=a[i]*a[i]*(1<<i);
    cout<<ans;
    return 0;
}

B.三角形周长和

考虑每条边的贡献。由于每个点两两间都有边,所以对于一条边,它会在个三角形中出现。

答案就是

#include<bits/stdc++.h>
using namespace std;
#define next Next
#define gc getchar
#define int long long
const int mod=998244353;
const int N=1e6+5;
int n,m,k,ans,a[N],b[N];
char s[N];
//char buf[1<<21],*p1=buf,*p2=buf;
//inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline int read()
{
    int ret=0,f=0;char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){ret=ret*10+c-48;c=gc();}
    if(f)return -ret;return ret;
}
signed main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read(),b[i]=read();
    }
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            ans=(ans+(abs(a[i]-a[j])+abs(b[i]-b[j]))%mod*(n-2)%mod)%mod;
    cout<<ans;
    return 0;
}

C.操作集锦

提供一个的做法。

考虑,设表示前个数,选了的方案数。

如果不考虑有重复的情况,那么

但是有重复的情况,怎么办?那就把重复的减去就好了。

重复了那些呢,记为:第个字符上一次出现的地方。

那么重复的就是。相当于多的方案就是加了个第个字符,但在的时候这个答案就已经算过了。

#include<bits/stdc++.h>
using namespace std;
#define next Next
#define gc getchar
#define int long long
const int mod=1e9+7;
const int N=1005;
int n,m,k,ans,f[N][N];
char s[N];
//char buf[1<<21],*p1=buf,*p2=buf;
//inline int gc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
inline int read()
{
    int ret=0,f=0;char c=gc();
    while(!isdigit(c)){if(c=='-')f=1;c=gc();}
    while(isdigit(c)){ret=ret*10+c-48;c=gc();}
    if(f)return -ret;return ret;
}
signed main()
{
    n=read();m=read();
    scanf("%s",s+1);
    f[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        f[i][0]=1;f[i][i]=1;
        int lst=0;
        for(int j=i-1;j;j--)
            if(s[j]==s[i])
            {
                lst=j;
                break;
            }
        for(int j=1;j<i;j++)
        {
            f[i][j]=f[i-1][j];
            if(lst)
            {
                f[i][j]+=f[i-1][j-1]-f[lst-1][j-1];
            }
            else f[i][j]+=f[i-1][j-1];
            f[i][j]=(f[i][j]%mod+mod)%mod;
        }
    }
    cout<<f[n][m];
    return 0;
}

D.斩杀线计算大师

好像不少都是用过的,这里介绍一种新的做法——同余最短路

同余最短路是什么?

就是每个点的意义是在模的意义下能被构造出来的最小值

这有什么用呢?

这可以用最短路的方法求的余数是的最小能构造出来的数

这就可一求中有多少数能被构造出来,即若干个的和

还不会的可以详见我的博客(内有例题和题解)https://blog.nowcoder.net/n/05b3573da70d414693afd7ec2b3fc5ce

这题和跳楼机几乎一样,有兴趣的可以先做那题。

这题就是在同余最短路的基础上记录一下上一个选的是啥,然后就能统计出每个数使用的次数了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+5;
int h,a,b,c,mi,dis[N],vis[N],gs[N];
struct node{
    int id,val;
}nxt[N];
bool operator < (node a,node b)
{
    return a.val>b.val;
}
priority_queue<node>q;
void dj()
{
    for(int i=0;i<N;i++)dis[i]=1e18;
    dis[0]=0;
    q.push((node){0,0});
    while(!q.empty())
    {
        int u=q.top().id;q.pop();
        if(vis[u])continue;
        vis[u]=1;
        if(dis[(u+a)%mi]>dis[u]+a)
        {
            dis[(u+a)%mi]=dis[u]+a;
            nxt[(u+a)%mi]=(node){u,1};
            q.push((node){(u+a)%mi,dis[(u+a)%mi]});
        }
        if(dis[(u+b)%mi]>dis[u]+b)
        {
            dis[(u+b)%mi]=dis[u]+b;
            nxt[(u+b)%mi]=(node){u,2};
            q.push((node){(u+b)%mi,dis[(u+b)%mi]});
        }
        if(dis[(u+c)%mi]>dis[u]+c)
        {
            dis[(u+c)%mi]=dis[u]+c;
            nxt[(u+c)%mi]=(node){u,3};
            q.push((node){(u+c)%mi,dis[(u+c)%mi]});
        }
    }
}
void work(int h)
{
    if(h==0)return;
    gs[nxt[h].val]++;
    h=nxt[h].id;
    work(h);
}
signed main()
{
    scanf("%lld%lld%lld",&a,&b,&c);
    scanf("%lld",&h);
    mi=max(a,max(b,c));
    dj();
    work(h%mi);
    int x=a*gs[1]+b*gs[2]+c*gs[3];
    x=h-x;
    if(mi==a)gs[1]+=x/a;
    else if(mi==b)gs[2]+=x/b;
    else if(mi==c)gs[3]+=x/c;
    cout<<gs[1]<<" "<<gs[2]<<" "<<gs[3];
    return 0;
}

E.旗鼓相当的对手

这题题意有点不清,有歧义,导致我wa了4发。

应该可以换根DP,我用的是长链剖分,这真是模板题啊。

维护表示从点出发向下的长度为的端点权值和。 表示从点出发向下的长度为的端点个数。

然后就可以用这两个数组来求答案了。


#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
const int N=1e6+5;
struct node{
    int too,next;
}edge[N*2];
int n,top,m,ans[N],a[N],head[N],len[N],son[N],tmp[N],*f[N],*g[N],*id=tmp;
void add(int a,int b)
{
    edge[++top].too=b;edge[top].next=head[a];head[a]=top;
}
void dfs(int u,int fa)
{
    for(int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].too;
        if(v==fa)continue;
        dfs(v,u);
        if(len[v]>len[son[u]])son[u]=v;
    }
    len[u]=len[son[u]]+1;
}
void dp(int u,int fa)
{
    if(son[u])
    {
        f[son[u]]=f[u]+1;
        g[son[u]]=g[u]+1;
        dp(son[u],u);
//      ans[u]+=ans[son[u]];
    }
    f[u][0]=a[u];
    g[u][0]=1;
    for(int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].too;
        if(v==fa||v==son[u])continue;
        f[v]=id;
        id+=len[v]*2;
        g[v]=id;
        id+=len[v]*2;
        dp(v,u);
//      ans[u]+=ans[v];
        for(int j=0;j<len[v];j++)
            if(m-j-1>0&&m-j-1<len[u])
            {
                ans[u]+=f[u][m-j-1]*g[v][j];
                ans[u]+=g[u][m-j-1]*f[v][j];
//              if(u!=1)continue;
//              cout<<j<<" "<<ans[u]<<" "<<f[u][j+1]<<" "<<f[v][m-j-2]<<endl;
            }
        for(int j=0;j<len[v];j++)
        {
            f[u][j+1]+=f[v][j];
            g[u][j+1]+=g[v][j];
        }
    }
}
signed main()
{
    int x,y;
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=1;i<n;i++)
    {
        scanf("%lld%lld",&x,&y);
        add(x,y);add(y,x);
    }
    dfs(1,0);
    f[1]=id;
    id+=len[1]*2;
    g[1]=id;
    id+=len[1]*2;
    dp(1,0);
    for(int i=1;i<=n;i++)printf("%lld ",ans[i]);
    return 0;
}
/*
7 2
1 2 3 4 5 6 7
1 2
2 3
2 4
1 5
5 6
5 7
*/