首页 > 其他分享 >CSP模拟14

CSP模拟14

时间:2023-08-06 20:34:09浏览次数:39  
标签:ch 14 int mid 模拟 dpl CSP dp dpr

不会暴力!不会暴力!


第负一题

分治+DP

只会 $ n^2 $ 暴力.

\(dpl[i][0/1] 向左 选/不选 mid 的最大值\)

\(dpr[i][0/1] 向右 选/不选 mid 的最大值\)

$ ans = \sum _{i=l} ^{mid} \sum _{j=mid+1} ^{r} max( dpl[i][0]+dpr[j][0],dpl[i][1]+dpr[j][0],dpr[i][0]+dpr[j][1] ) ,但这个形式并不好处理.$

$ 可以转化为 ans = \sum _{i=l} ^{mid} \sum _{j=mid+1} ^{r} max(dpl[i][1]-dpl[i][0],dpr[j][1]-dpr[j][0],0) +dpl[i][0]+dpr[j][0] (直接拆出去加减就好) 发现依旧不好处理 $

$ 设 fl[i] = max _{i=l}^{mid} (dpl[i][1]-dp[i][0],0),fr[i] =max _{i=mid+1}^{r} (dpr[i][1]-dpr[i][0],0) $

$ ans = \sum _{i=l} ^{mid} \sum _{j=mid+1} ^{r} max(fl[i],fr[j])+dpl[i][0]+dpr[j][0] $

分治很妙,把\(i,j\)分开并各自统一的转化很妙也很关键.

Code
#include<cstdio>
#include<algorithm>
#include<cstring>

#define int long long
using namespace std;
const int N=2e5+5;
const int mod=998244353;

inline int read(){
    int x=0,f=1;
    char ch=getchar();

    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}

inline int max(int a,int b){
    return a>b?a:b;
}

int n;
int a[N];
int ans;
int dp[N][2];
int dpl[N][2],dpr[N][2];
int fl[N],fr[N];

void work1(int l,int r,int mid){
    dp[mid][0]=0,dp[mid][1]=-mod;
    for(int i=mid-1;i>=l;i--){
        dp[i][0]=max(dp[i+1][1],dp[i+1][0]);
        dp[i][1]=dp[i+1][0]+a[i];
        dpl[i][0]=max(dp[i][0],dp[i][1]);
    }

    dp[mid][0]=-mod,dp[mid][1]=a[mid];
    for(int i=mid-1;i>=l;i--){
        dp[i][0]=max(dp[i+1][1],dp[i+1][0]);
        dp[i][1]=dp[i+1][0]+a[i];
        dpl[i][1]=max(dp[i][0],dp[i][1]);
    }

    dp[mid+1][0]=0,dp[mid+1][1]=-mod;
    for(int i=mid+2;i<=r;i++){
        dp[i][0]=max(dp[i-1][0],dp[i-1][1]);
        dp[i][1]=dp[i-1][0]+a[i];
        dpr[i][0]=max(dp[i][0],dp[i][1]);
    }
    
    dp[mid+1][0]=-mod,dp[mid+1][1]=a[mid+1];
    for(int i=mid+2;i<=r;i++){
        dp[i][0]=max(dp[i-1][0],dp[i-1][1]);
        dp[i][1]=dp[i-1][0]+a[i];
        dpr[i][1]=max(dp[i][0],dp[i][1]);
    }

    dpl[mid][0]=dpr[mid+1][0]=0;
    dpl[mid][1]=a[mid],dpr[mid+1][1]=a[mid+1];
}

void work2(int l,int r,int mid){
    for(int i=l;i<=mid;i++){
        fl[i]=max(dpl[i][1]-dpl[i][0],0);
        ans=(ans+(r-mid)*dpl[i][0]%mod)%mod;
    }
    for(int i=mid+1;i<=r;i++){
        fr[i]=max(dpr[i][1]-dpr[i][0],0);
        ans=(ans+(mid-l+1)*dpr[i][0]%mod)%mod;
    }

    sort(fl+l,fl+mid+1);
    sort(fr+mid+1,fr+r+1);

    int ql=l,qr=mid+1;

    while(ql<=mid){
        while(qr<=r&&fr[qr]<=fl[ql]){
            ans=(ans+fr[qr]*(ql-l)%mod)%mod;
            ++qr;
        }
        ans=(ans+fl[ql]*(qr-mid-1)%mod)%mod;
        ++ql;
    }
    while(qr<=r){
        ans=(ans+fr[qr]*(mid-l+1)%mod)%mod;
        ++qr;
    }
}

void solve(int l,int r){
    if(l==r){
        ans=(ans+a[l])%mod;
        return ;
    }

    int mid=(l+r)>>1;
    solve(l,mid),solve(mid+1,r);

    work1(l,r,mid);
    work2(l,r,mid);
}

signed main(){
    n=read();

    for(int i=1;i<=n;i++)
        a[i]=read();
    
    solve(1,n);
    
    printf("%lld ",ans%mod);
    
    return 0;
}

数树

暴力只会枚举排列,链和外向树都不会

正解是 容斥+树上背包

直接求限制太多,我们求非法的方案数$ sum _i $ 总方案 $ (n-i)!sum_i $ 容斥系数由二项式反演得出.只是系数不一样.

$ 设 dp[i][j][0/1/2/3] 表示 节点 i ,选了 j 条边 0:独立节点,1:有入边,2:有出边,3:既有入边又有出边. sum_i = \sum _{k=0}^{3} dp[1][i][k] $

树上背包就好,一个合并子树的过程.

Code
#include<cstdio>
#include<cstring>

using namespace std;
const int N=5005;
const int mod=998244353;

inline int read(){
    int x=0,f=1;
    char ch=getchar();

    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}

struct Node{
    int to,next,typ;
}e[N<<1];
int lk[N],ntot;

void add(int x,int y,int typ){
    e[++ntot]=(Node){y,lk[x],typ};
    lk[x]=ntot;
}

int n;
int fac[N];
int siz[N];
int dp[N][N][4],dp1[N][4];

void work(int x,int y,int typ){
    memset(dp1,0,sizeof(dp1));
    for(int i=0;i<=siz[x]-1;i++){
        for(int j=0;j<=siz[y]-1;j++){
            int sum=0;

            for(int k=0;k<=3;k++)
                sum=(sum+dp[y][j][k])%mod;
            for(int k=0;k<=3;k++)
                dp1[i+j][k]=(dp1[i+j][k]+1ll*sum*dp[x][i][k]%mod)%mod;
            
            if(typ==0){
                dp1[i+j+1][1]=(dp1[i+j+1][1]+1ll*dp[x][i][0]*(dp[y][j][0]+dp[y][j][1])%mod)%mod;
                dp1[i+j+1][3]=(dp1[i+j+1][3]+1ll*dp[x][i][2]*(dp[y][j][0]+dp[y][j][1])%mod)%mod;
            }else{
                dp1[i+j+1][2]=(dp1[i+j+1][2]+1ll*dp[x][i][0]*(dp[y][j][0]+dp[y][j][2])%mod)%mod;
                dp1[i+j+1][3]=(dp1[i+j+1][3]+1ll*dp[x][i][1]*(dp[y][j][0]+dp[y][j][2])%mod)%mod;
            }
        }
    }

    siz[x]+=siz[y];

    for(int i=0;i<=siz[x];i++){
        for(int k=0;k<=3;k++){
            dp[x][i][k]=dp1[i][k];
        }
    }
}

void dfs(int x,int fa){
    siz[x]=1;
    dp[x][0][0]=1;

    for(int i=lk[x];i;i=e[i].next){
        int y=e[i].to;
        
        if(y==fa)
            continue;
        dfs(y,x);
        work(x,y,e[i].typ);
    }
}

int main(){
    // freopen("in","r",stdin);

    n=read();

    for(int i=1;i<=n-1;i++){
        int x=read(),y=read();
        add(x,y,1),add(y,x,0);
    }

    fac[0]=1;
    for(int i=1;i<=n;i++){
        fac[i]=1ll*fac[i-1]*i%mod;
    }

    dfs(1,0);
    
    int ans=0;
    
    for(int i=0,opt=1;i<=n-1;i++,opt=-opt){
        int sum=0;

        for(int k=0;k<=3;k++)
            sum=(sum+dp[1][i][k])%mod;
        ans=(ans+1ll*opt*fac[n-i]*sum%mod+mod)%mod;
    }

    printf("%d ",ans);

    return 0;
}


石子游戏

考场 $ n^2 $ 都没手完出来……

dp挺冷门的.

题解


子段和

暴力不会,正解不会.

标签:ch,14,int,mid,模拟,dpl,CSP,dp,dpr
From: https://www.cnblogs.com/yszy/p/17609882.html

相关文章

  • CSP模拟13
    T1考场降智,写了个假的模拟,没签上到。T3空间爆了,直接CE(应该是线段树写挂了).yxt在四个角,取最大值,排序.Codefor(inti=1;i<=n;i++){for(intj=1;j<=m;j++){a[++tot]=max({calc(1,1,i,j),calc(i,j,1,m),calc(i,j,n,1),calc(i,j,n,m)});......
  • 模拟退火
    模拟退火(SimulateAnneal)是一种用于解决问题方案数极大且非单峰函数的随机化算法,原理与金属退火类似。每次随机出一个新解,若新解更优则接受,否则以一个与温度和与最优解的差相关的概率接受它。降温模拟退火有三个参数:初始温度\(T_0\),降温系数\(\Delta\),终止温度\(T_k\).其中......
  • 【LGR-148-Div.3】洛谷基础赛 #1 & MGOI Round I
    【LGR-148-Div.3】洛谷基础赛#1&MGOIRoundI据说是普及组难度?T1P9502『MGOI』SimpleRoundI|A.魔法数字\(100pts\)题目描述初级魔法士小M的魔法数字是\(2\)。给定一个正整数\(n\),小M需要找到最大的偶数\(m\),使得\(2^m<n\)。又双叒叕是个水题,然后被又双......
  • HS8145C5光猫桥接与路由器拨号
    前言  前几天在外边连接家里的设备总是提示离线,最奇怪的是离线时间段集中在某一个时间范围内,一度怀疑是网络不稳定造成的。  我起初的想法是,写个程序用来监控网络状况,一旦检测到异常及时向我发送通知,但是刘老哥建议我把光猫改成桥接用路由器拨号,每天定时重启一下得了。试了......
  • 【LGR-148-Div.3】洛谷基础赛 #1 & MGOI Round I
    T1简单题,题面十分清晰,就是给我们\(n\),要求使\(2^m<n\)成立的最小偶数\(m\)。(要注意\(log_2N=m,m|2\)的情况)#include<bits/stdc++.h>#definelllonglong#definereregisterusingnamespacestd;constintN=800,INF=0x3f3f3f3f;lln;intmain(){ cin>>n; llk=log......
  • 模拟赛记录
    ZROI暑假集训T1给定序列\(a_i\)和\(d\),找出最长的子区间使得区间内元素排序后相差不超过\(d\)。人类智慧题两个限制:编号连续和差值的限制,两个分开维护都好做,但是结合起来比较麻烦,考虑每次把区间按照其中一个限制处理得到若干小区间,再把这些小区间按另一个限制处理,直到得......
  • 14_systemd
    systermd大多数现代Linux发行版都使用systermd(systermmanagementdeamon)作为默认的初始系统和服务管理器.systermd是Linux初始化系统管理、日志记录、启动管理器等等。systermd是第一个进程,它会接管并继续挂载主机文件系统和启动服务。在进程列表中,为了向后兼容,列表把syst......
  • 前端学习笔记202307学习笔记第六十一天-react知识点串讲之14
        ......
  • 前端学习笔记202307学习笔记第六十一天-模拟实现拦截器功能3
    前端        ......
  • 前端学习笔记202307学习笔记第六十一天-模拟实现拦截器功能2
       ......