CSP 模拟 5

T1 第一题

贪心,观察肯定是从较浅的点上来一个士兵或者从根节点来一个士兵,用 set 或者 vector 启发式合并维护这个过程即可

点击查看代码
#include<bits/stdc++.h>
#define N 100005
#define inf 0x3f3f3f3f
#define pii pair<int,int>
#define mp make_pair
using namespace std;

int n,head[N],tot,dep[N],mdep[N],ans;
vector<int> qwq[N];
struct edge{
    int v,nxt;
}e[N*2];
inline void add(int u,int v){
    e[++tot].v=v;
    e[tot].nxt=head[u];
    head[u]=tot;
}
void dfs(int u,int fa){
    dep[u]=dep[fa]+1;mdep[u]=dep[u];
    bool tmp=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(v==fa) continue;
        dfs(v,u);tmp=0;
        mdep[u]=max(mdep[u],mdep[v]);
        for(int j:qwq[v]) qwq[u].push_back(j);
    }
    if(tmp){
        qwq[u].push_back(dep[u]);
        ans+=dep[u];return;
    }
    sort(qwq[u].begin(),qwq[u].end(),greater<int>());
    int cnt=0;
    for(int i=qwq[u].size()-1;i>=1;i--){
        if(qwq[u][i]-dep[u]*2>=0) break;
        ans+=qwq[u][i]-dep[u]*2;cnt++;
    }
    while((cnt--)&&qwq[u].size()) qwq[u].pop_back();
}

int main(){
    cin>>n;
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dep[0]=-1;dfs(1,0);
    printf("%d",ans);
}

T2 第二题

这个答案具有单调性显然,考虑决定答案瓶颈的数即可

二分答案,每次用类似 spfa 的方式,首先把所有数都压进一个队列中,每次将队首的数和周围的 6 个数的差值补到 \(mid\),如果一个节点被更新则压入队列继续更新,直到队列为空,然后比较操作次数和 \(k\) 的大小即可

点击查看代码
#include<bits/stdc++.h>
#define N 100005
#define pii pair<int,int>
#define mp make_pair
#define int long long
#define inf 0x3f3f3f3f
using namespace std;

int r,c,k,maxn,minn=inf,sum,vis[N];
int mvx[6]={0,0,-1,-1,1,1},mxy[6]={-1,1,0,1,-1,0};
vector<int> a[N],b[N];
inline int id(int x,int y){return c*(x-1)+y;}
bool check(int mid){
    queue<pii> q;
    memset(vis,1,sizeof(vis));
    for(int i=1;i<=r;i++){
        b[i]=a[i];
        for(int j=1;j<=c;j++)
            q.push(mp(i,j));
    }
    int tmp=0;
    while(!q.empty()){
        int x=q.front().first,y=q.front().second;
        q.pop();vis[id(x,y)]=false;
        int Maxn=0;
        for(int i=0;i<6;i++){
            int tx=x+mvx[i],ty=y+mxy[i];
            if(tx<1||tx>r||ty<1||ty>c) continue;
            Maxn=max(Maxn,b[tx][ty]);
        }
        if(Maxn-b[x][y]>mid){
            tmp+=Maxn-b[x][y]-mid;
            b[x][y]=Maxn-mid;
        }
        for(int i=0;i<6;i++){
            int tx=x+mvx[i],ty=y+mxy[i];
            if(tx<1||tx>r||ty<1||ty>c) continue;
            if(abs(b[x][y]-b[tx][ty])>mid){
                tmp+=b[x][y]-b[tx][ty]-mid;
                b[tx][ty]=b[x][y]-mid;
                if(!vis[id(tx,ty)]){
                    q.push(mp(tx,ty));
                    vis[id(tx,ty)]=true;
                }
            }
        }
        if(tmp>k) return false;
    }
    return true;
}

signed main(){
    // srand(time(0)^*(new unsigned int));
    cin>>r>>c>>k;
    for(int i=1;i<=r;i++){
        a[i].push_back(0);
        for(int j=1;j<=c;j++){
            int x;scanf("%lld",&x);
            maxn=max(maxn,x);minn=min(minn,x);
            a[i].push_back(x);
        }
    }
    int l=0,r=maxn-minn,ans=maxn-minn;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(mid)) r=mid-1,ans=mid;
        else l=mid+1;
    }
    printf("%lld\n",ans);
}

T3 第三题

数位 dp,通常的数位 dp 为考虑是否压住上界,这个是考虑是否压住上下界

设状态 \(f_{i,j,0/1,0/1}\) 为选到第 \(i\) 位,选了 \(j\) 个 0,是否压住上下界,统计 \(a-1\sim \infty\)\(b\sim \infty\) 的方案数相减即可

点击查看代码
代码

第四题

\(k^2\) 拆成 \(2\binom k2 +k\),就变为统计值都为 \(x\) 的点对数乘 2 和值为 \(x\) 的点数

\(f_{i,j}\) 为选到第 \(i\) 个位置,最大值为 \(j\) 的方案数,\(g_{i,j}\) 为从 \(i\) 位置,最大值为 \(j\) 的情况下选到末尾的方案数

\(i\) 位置对 \(x\) 的贡献为:

\[\sum_{y\ge x} f_{i-1,y}(g_{i+1,j}+2(n-i)g_{i+2,j})\\+f_{i-1,x-1}(g_{i+1,x}+2(n-i)g_{i+2,x}) \]

即为将 \(x\) 填到 \(i\) 位置,再考虑将前边和后边合起来,考虑对后边出现次数和点对的影响

点击查看代码
#include<bits/stdc++.h>
#define N 3005
#define int long long
using namespace std;

int n,m,ans[N];
int f[N][N],g[N][N],sum[N][N];

signed main(){
    cin>>n>>m;
    f[0][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            f[i][j]=(f[i-1][j]*j+f[i-1][j-1])%m;
    for(int i=1;i<=n;i++) g[n+1][i]=1;
    for(int i=n;i>=1;i--)
        for(int j=n;j>=1;j--)
            g[i][j]=(g[i+1][j]*j+g[i+1][j+1])%m;
    for(int i=1;i<=n;i++)
        for(int j=n;j>=1;j--)
            sum[i][j]=(sum[i][j+1]+f[i-1][j]*(g[i+1][j]+2*(n-i)%m*g[i+2][j]%m))%m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            ans[j]=(ans[j]+sum[i][j]+f[i-1][j-1]*(g[i+1][j]+2*(n-i)%m*g[i+2][j]%m)%m)%m;
    for(int i=1;i<=n;i++)
        printf("%lld ",ans[i]);
}
posted @ 2023-07-27 18:51  Rolling_star  阅读(36)  评论(0编辑  收藏  举报