牛客练习赛56

题目:

牛客练习赛56

A:小蒟和他的乐谱

解法:

百度一下什么是升降八度后直接暴力

B:小琛和他的学校

解法:

枚举每条边,维护这条边两边的节点个数和学生的个数直接计算答案即可

代码:

#include 
  
#define x first
#define y second
#define pii pair
#define sz(x) (int)(x).size()
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int mod = 998244353;
const int maxn = 2e5+25;
int head[maxn],a[maxn],cnt,u,v,w,n,sum;
struct edge{
    int to,nxt,w,id;
}e[maxn<<1];
inline void add(int u,int v,int w,int id){
    e[++cnt]= (edge){v,head[u],w,id};
    head[u] = cnt;
}
int sz[maxn],num[maxn];
LL ans[maxn];
void dfs(int x,int fa){
    sz[x] = 1; num[x] = a[x];
    for(int i = head[x];i > 0;i = e[i].nxt){
        int v = e[i].to;
        if(v == fa) continue;
        dfs(v,x); sz[x] += sz[v]; num[x] += num[v];
        ans[e[i].id] = 2ll*e[i].w*(1ll*sz[v]*(sum-num[v])+1ll*num[v]*(n-sz[v]));
    }
}
int main(){
    scanf("%d",&n);
    for(int i = 1;i <= n; ++i){
        scanf("%d",a+i); sum += a[i];
    }
    for(int i = 1;i < n; ++i){
        scanf("%d %d %d",&u,&v,&w);
        add(u,v,w,i); add(v,u,w,i);
    }
    dfs(1,0);
    for(int i = 1;i < n; ++i) printf("%lld\n",ans[i]);
    return 0;
}

C:小魂和他的数列

解法:

离散化后树状数组优化 dp 即可

代码:

#include 
  
#define x first
#define y second
#define pii pair
#define sz(x) (int)(x).size()
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int mod = 998244353;
const int maxn = 5e5+55;
int c[maxn],cnt,n,k,a[maxn],tr[maxn][11];
inline int getid(int x){
    return lower_bound(c+1,c+cnt,x) - c;
}
inline int lowbit(int x){
    return x & (-x);
}
int persum(int pos,int k){
    LL sum = 0;
    if(k == 0) return 1;
    while(pos){
        sum += tr[pos][k];
        pos -= lowbit(pos);
    }
    return sum%mod;
}
void add(int pos,int k,int v){
    while(pos < cnt){
        tr[pos][k] += v;
        if(tr[pos][k] >= mod) tr[pos][k] -= mod;
        pos += lowbit(pos);
    }
}
int main(){
    scanf("%d %d",&n,&k);
    for(int i = 1;i <= n; ++i){
        scanf("%d",a+i);
        c[i] = a[i];
    }
    sort(c+1,c+n+1);
    cnt = unique(c+1,c+n+1) - c;
    int ans = 0;
    for(int i = 1;i <= n; ++i){
        int pos = getid(a[i]);
        for(int j = 1;j <= k; ++j){
            int v = persum(pos-1,j-1);
            if(v) add(pos,j,v);
        }
        ans += persum(pos-1,k-1);     //统计以a[i]为k个递增子序列的最后一个的方案数
        if(ans >= mod) ans -= mod;
    }
    printf("%d\n",ans);
    return 0;
}

D:小翔和泰拉瑞亚

解法:

枚举第 x 列在最后是最矮的,如果最后最矮的那一列和最高的那一列会被一个操作同时减 w,但是差值是不会变的;意味着我们只需要把包含 x 的操作全部执行一遍后找到另一个最大值即可更新答案;从小到大依次枚举 x,考虑每个操作影响的区间,当 x = L 时,执行操作,当 x  = R + 1,取消该操作,每次查询区间最值即可;

代码:

#include 
  
#define x first
#define y second
#define pii pair
#define sz(x) (int)(x).size()
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int mod = 998244353;
const int maxn = 2e5+25;
int n,m,L,R,w,h[maxn];
std::vector vL[maxn],vR[maxn];
LL maxv[maxn<<2],minv[maxn<<2],lazy[maxn<<2];
void build(int l,int r,int x){
    if(l == r){
        maxv[x] = minv[x] = h[l];
        return ;
    }
    int mid = (l+r) >> 1;
    build(l,mid,x<<1);
    build(mid+1,r,x<<1|1);
    maxv[x] = Max(maxv[x<<1],maxv[x<<1|1]);
    minv[x] = Min(minv[x<<1],minv[x<<1|1]);
}
inline void pushdown(int x){
    minv[x<<1] += lazy[x];
    maxv[x<<1] += lazy[x];
    lazy[x<<1] += lazy[x];
    minv[x<<1|1] += lazy[x];
    maxv[x<<1|1] += lazy[x];
    lazy[x<<1|1] += lazy[x];
    lazy[x] = 0;
}
void updata(int l,int r,int L,int R,int x,int val){
    if(l > R || r < L) return ;
    if(l >= L && r <= R){
        minv[x] += val;
        maxv[x] += val;
        lazy[x] += val;
        return ;
    }
    if(lazy[x]) pushdown(x);
    int mid = (l+r) >> 1;
    updata(l,mid,L,R,x<<1,val);
    updata(mid+1,r,L,R,x<<1|1,val);
    maxv[x] = Max(maxv[x<<1],maxv[x<<1|1]);
    minv[x] = Min(minv[x<<1],minv[x<<1|1]);
}
inline int read() {
    int x = 0, f = 1;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
    return x * f;
}
inline void write(LL x) {
    if (x >= 10) write(x / 10);
    putchar(x % 10 + '0');
}
int main(){
    n = read(), m = read();
    for(int i = 1;i <= n; ++i) h[i] = read();
    build(1,n,1);
    for(int i = 1;i <= m; ++i){
        L = read(), R = read(), w = read();
        vL[L].push_back(pii(R,-w));
        vR[R].push_back(pii(L,w));
    }
    LL ans = 0;
    for(int i = 1;i <= n; ++i){
        for(int j = 0;j < sz(vL[i]); ++j){
            updata(1,n,i,vL[i][j].x,1,vL[i][j].y);
        }
        ans = Max(ans,maxv[1]-minv[1]);
        for(int j = 0;j < sz(vR[i]); ++j){
            updata(1,n,vR[i][j].x,i,1,vR[i][j].y);
        }
    }
    write(ans);
    return 0;
}

E:小雀和他的王国

解法:

环上的边永远不会有影响,Tanjar 缩环后变成一棵树,求出树的直径即可,增加的边一定是连接直径的两个端点

代码:

#include 
 
#define x first
#define y second
#define pii pair
#define sz(x) (int)(x).size()
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int mod = 1e9+7;
const int maxn = 2e5+25;
int n,m,u,v;
struct edge{
    int to,nxt;
}e[maxn<<1],E[maxn<<1];
int head[maxn],cnt,Head[maxn],Cnt;
inline void add(int u,int v){
    e[++cnt] = (edge){v,head[u]};
    head[u] = cnt;
}
inline void Add(int u,int v){
    E[++Cnt] = (edge){v,Head[u]};
    Head[u] = Cnt;
}
int dfn[maxn],low[maxn],id[maxn],Isstack[maxn],num,ID;
stack s; 
void Tarjan(int x,int fa){                   //缩环
    dfn[x] = low[x] = ++num;
    s.push(x); Isstack[x] = 1; int cnt = 0;
    for(int i = head[x];i > 0;i = e[i].nxt){
        int v = e[i].to;
        if(v == fa && !cnt){cnt++; continue;}
        if(!dfn[v]){
            Tarjan(v,x);
            low[x] = Min(low[x],low[v]);
        }
        else if(Isstack[v]) low[x] = Min(low[x],dfn[v]);
    }
    if(dfn[x] == low[x]){
        ID++; int v;
        do{
            v = s.top(); s.pop();
            Isstack[v] = 0; id[v] = ID;
        }while(v != x);
    }
}
int dp[maxn][2],ans,b,vis[maxn];
void dfs1(int x,int fa){                  //重新建边
    vis[x] = 1;
    for(int i = head[x];i > 0; i = e[i].nxt){
        int v = e[i].to; if(v == fa) continue;
        if(id[x] != id[v]){
            Add(id[x],id[v]);
            Add(id[v],id[x]);
            b++;
        }
        if(!vis[v]) dfs1(v,x);
    }
}
void dfs2(int x,int fa){                    //求树的直径
    for(int i = Head[x];i > 0;i = E[i].nxt){
        int v = E[i].to; if(v == fa) continue;
        dfs2(v,x);
        if(dp[v][0]+1 > dp[x][0]){
            dp[x][1] = dp[x][0];
            dp[x][0] = dp[v][0]+1;
        }
        else dp[x][1] = Max(dp[x][1],dp[v][0]+1);
    }
    ans = Max(ans,dp[x][0]+dp[x][1]);
}
LL qpow(LL a,LL x,LL mod){
    LL res = 1ll;
    while(x){
        if(x&1) res = res * a % mod;
        a = a * a % mod;
        x >>= 1;
    }
    return res;
}
int main(){
    scanf("%d %d",&n,&m);
    for(int i = 1;i <= m; ++i){
        scanf("%d %d",&u,&v);
        add(u,v); add(v,u);
    }
    Tarjan(1,0); dfs1(1,0); dfs2(1,0);
    printf("%d\n",1ll*(b-ans)*qpow(m+1,mod-2,mod)%mod);
    return 0;
}

 

 

你可能感兴趣的:(题解----牛客网)