牛客练习赛56(A(水),B(树上路径问题),C(权值树状数组维护子序列),D(枚举加扫描线段树),E(tarjan缩点+求直径))

题目链接

A 小蒟和他的乐谱

水题,取下模就可以了。

#include
using namespace std;
typedef long long ll;
const int N=1e6+10;
int a[N],b[N];
int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
        if(a[i]<=0) {
            a[i]=a[i]%7+7;
        }
        else if(a[i]>7){
            a[i]=a[i]%7;
            if(a[i]==0) a[i]=7;
        }
        if(a[i]!=4&&a[i]!=7) b[i]=1;
    }
 
    int ans=0;
 
    int pre=0;
    for(int i=1;i<=n;++i){
        if(b[i]) pre++;
        else{
            ans=max(ans,pre);
            pre=0;
        }
    }
    ans=max(ans,pre);
    printf("%d\n",ans);
}

B-小琛和他的学校

水题加一

枚举每条边,那么这条边的花费值就是(左边人数*右边点数+右边点数*左边点数)*这条边权值

#include
using namespace std;
typedef long long ll;
 
const int N=2e5+10;
ll ans[N];
ll a[N],dp[N],sz[N];
int n,cnt;
int head[N];
ll sum;
struct edge
{
    int v,ne,w,id;
}e[N*2];
void dfs(int u,int fa)
{
    dp[u]=a[u];
    sz[u]++;
    for(int i=head[u];i;i=e[i].ne){
        int v=e[i].v,w=e[i].w;
        if(v==fa) continue;
        dfs(v,u);
        dp[u]+=dp[v];
        sz[u]+=sz[v];
 
        ans[e[i].id]=2ll*(dp[v]*(n-sz[v])+(sum-dp[v])*sz[v])*w;
        //printf("id:%d %lld %lld %lld %lld\n",e[i].id,dp[v],n-sz[v],sum-dp[v],sz[v]);
    }
}
void add(int u,int v,int w,int id)
{
    e[++cnt]={v,head[u],w,id};
    head[u]=cnt;
 
    e[++cnt]={u,head[v],w,id};
    head[v]=cnt;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%lld",&a[i]);
        sum+=a[i];
    }
 
    for(int i=1;i

C-小魂和他的数列

由于k<=10,那么对于k=1,2,。。k每个长度都弄一个权值树状数组维护个数。

每次新增一个值,那么就求k-1的和来更新k这个树状数组里面a[i]所在的权值下标即可。

#include
using namespace std;
typedef long long ll;
const int N=5e5+10;
int tr[15][N];
int a[N],n,k,X[N],len;
const int mod=998244353;
int getid(int x)
{
    return lower_bound(X+1,X+1+len,x)-X;
}
int low(int x)
{
    return x&(-x);
}
void add(int ty,int x,ll val)
{
    for(int i=x;i<=len;i+=low(i)){
        tr[ty][i]+=val;
        tr[ty][i]%=mod;
    }
}
int qu(int ty,int x)
{
    ll res=0;
    for(int i=x;i;i-=low(i)) {
        res=(res+tr[ty][i])%mod;
    }
    return res;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i) {
        scanf("%d",&a[i]);X[i]=a[i];
    }

    sort(X+1,X+1+n);
    len=unique(X+1,X+1+n)-X-1;
    for(int i=1;i<=n;++i){
        int id=getid(a[i]);
        ll sum;
        for(int j=1;j<=k;++j){
            if(j==1) sum=1;
            else {
                sum=qu(j-1,id-1);
            }
            add(j,id,sum);
        }
    }
    printf("%d\n",qu(k,len));
}

D-小翔和泰拉瑞亚

牛客练习赛56(A(水),B(树上路径问题),C(权值树状数组维护子序列),D(枚举加扫描线段树),E(tarjan缩点+求直径))_第1张图片

#include
#define fi first
#define se second
#define mid (l+r>>1)
#define ls  (id<<1)
#define rs  (id<<1|1)

using namespace std;
typedef long long ll;
const int N=2e5+10;
ll mx[4*N],mi[4*N],sum[4*N],lazy[4*N],a[N];
int n,m;
struct node
{
    int l,r,w;
}b[N];
vectoradd[N],del[N];
void build(int id,int l,int r)
{
    if(l==r){
        scanf("%lld",&a[l]);
        mx[id]=mi[id]=a[l];
        return ;
    }
    build(ls,l,mid);
    build(rs,mid+1,r);
    mx[id]=max(mx[ls],mx[rs]);
    mi[id]=min(mi[ls],mi[rs]);
}
void pushdown(int id)
{
    if(lazy[id]){
        lazy[ls]+=lazy[id];
        lazy[rs]+=lazy[id];
        mi[ls]+=lazy[id];mi[rs]+=lazy[id];
        mx[ls]+=lazy[id];mx[rs]+=lazy[id];

        lazy[id]=0;
    }
}
void up(int id,int l,int r,int ql,int qr,int w)
{
    if(ql<=l&&r<=qr){
        mx[id]+=w;
        mi[id]+=w;
        lazy[id]+=w;
        return ;
    }
    pushdown(id);
    if(ql<=mid) up(ls,l,mid,ql,qr,w);
    if(qr>mid) up(rs,mid+1,r,ql,qr,w);
    mx[id]=max(mx[ls],mx[rs]);
    mi[id]=min(mi[ls],mi[rs]);
}
int main()
{
    scanf("%d%d",&n,&m);
    build(1,1,n);
    for(int i=1;i<=m;++i){
        int l,r,w;
        scanf("%d%d%d",&l,&r,&w);
        b[i]={l,r,w};
        add[l].push_back(i);
        del[r+1].push_back(i);
    }
    ll ans=mx[1]-mi[1];
    for(int i=1;i<=n;++i){
        for(int v:add[i]) up(1,1,n,b[v].l,b[v].r,-b[v].w);
        for(int v:del[i]) up(1,1,n,b[v].l,b[v].r,b[v].w);
        ans=max(ans,mx[1]-mi[1]);
    }
    printf("%lld\n",ans);
}

E-小雀和他的王国

牛客练习赛56(A(水),B(树上路径问题),C(权值树状数组维护子序列),D(枚举加扫描线段树),E(tarjan缩点+求直径))_第2张图片

注意题目数据存在重边,有重边,那也是一个强连通。。。

#include
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll powmod(ll a,ll b){a=a%mod;ll res=1;for(;b;b>>=1){
if(b&1) res=res*a%mod;a=a*a%mod;}return res%mod  ;}
const int N=2e5+10;
int cnt,dfn[N],low[N],n,m,bcc[N],bccnum;
int dp[N];

vectorG[N],g[N];
stacksta;
void tarjan(int id,int fa)
{
    sta.push(id);
    dfn[id]=low[id]=++cnt;
    int flag=0;
    for(int v:G[id]){
        if(v == fa && !flag){flag++; continue;}//考虑重边
        if(!dfn[v]){
            tarjan(v,id);
            low[id]=min(low[id],low[v]);
        }
        else low[id]=min(low[id],dfn[v]);
    }
    if(low[id]==dfn[id]){
        int x;
        ++bccnum;
        do{
            x=sta.top(),sta.pop();
            bcc[x]=bccnum;
        }while(x!=id);
    }
}
ll mx,tar;
void DP(int u,int fa)
{
    if(dp[u]>mx){
        mx=dp[u],tar=u;
    }

    for(int v:g[u]){
        if(v==fa) continue;
        dp[v]=dp[u]+1;
        DP(v,u);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i){
        int u,v;
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for(int i=1;i<=n;++i) if(!dfn[i])tarjan(i,0);

    for(int i=1;i<=n;++i){
        for(int v:G[i]){
            if(bcc[i]==bcc[v]) continue;
            g[bcc[i]].push_back(bcc[v]);
        }
    }

    DP(1,0);
    memset(dp,0,sizeof(dp));
    mx=0;
    DP(tar,0);
    ll ans=(1ll*bccnum-1-mx)*powmod(m+1,mod-2)%mod;
    cout<

 

你可能感兴趣的:(图论--tarjan算法,数据结构---线段树,牛客题解)