NOIP提高组模拟赛16

A. 茅山道术

简单DP,只考虑相邻两个同色的是否使用就行

code
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=1000005;
const int mod=1e9+7;
int rem[maxn],c[maxn],las[maxn],pos[maxn],n;
bool vis[maxn];
int work(int x){
    if(x>n)return 1;
    if(vis[x])return rem[x];
    vis[x]=1;
    if(pos[x]&&pos[x]!=x+1)return rem[x]=(1ll*work(pos[x])+work(x+1))%mod;
    return rem[x]=work(x+1);
}
int main(){
    freopen("magic.in","r",stdin);
    freopen("magic.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&c[i]);
    for(int i=n;i>=1;--i){
        pos[i]=las[c[i]];
        las[c[i]]=i;
    }
    int aas=work(1);
    printf("%d\n",aas);
    return 0;
}

B. 泰拳警告

这题不难,如果你知道组合数的一些性质会更加简单,不会像我一样还要想垃圾dp

期望=概率*得分=总得分/总方案数

把平局看成p种方案,这样算出总得分最后乘以(p+2)n的逆元即可

得分与平局数相关,考虑枚举平局数k,假设我们知道剩下nk轮中,只考虑胜负,胜局多于负局的方案数f[nk]

那么总得分为k=0n(k+1)×f[nk]×Cnk(可以在n轮中任意k轮平局,所以需要乘上Cnk)

剩下的问题变成求解f[nk]

不妨求f[i]

当前这一轮只有两种情况胜或负

如果当前胜,那么需要知道前i1轮 胜场>=负场 的方案数

也就是 f[i1]+(i1)轮 胜场等于负场 的方案数

如果i1是偶数那么后者的方案数就是Ci1(i1)/2,如果i1是奇数,那么不存在后者

如果当前负,那么需要知道前i1轮,胜场>负场+1 的方案数

也就是 f[i1](i1)轮 胜场 等于 负场+1 的方案数

如果i1是奇数那么后者的方案数就是Ci1(i1)/2,如果i1是偶数,那么不存在后者

这样就能解出f[]

code
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=3000005;
const int mod=998244353;

int jc[maxn],inv[maxn],n,p;

int qpow(int x,int y){
    int ans=1;
    while(y){
        if(y&1)ans=1ll*ans*x%mod;
        x=1ll*x*x%mod;
        y>>=1;
    }
    return ans;
}

void  ycl(){
    jc[0]=jc[1]=inv[0]=1;
    for(int i=2;i<=n;++i)jc[i]=1ll*jc[i-1]*i%mod;
    inv[n]=qpow(jc[n],mod-2);
    for(int i=n-1;i>=1;--i)inv[i]=1ll*inv[i+1]*(i+1)%mod;
}

int get_C(int n,int m){return 1ll*jc[n]*inv[n-m]%mod*inv[m]%mod;}

int f[maxn];


int main(){
   freopen("fight.in","r",stdin);
   freopen("fight.out","w",stdout);
   scanf("%d%d",&n,&p);
   ycl();
   f[1]=f[2]=1;
   for(int i=3;i<=n;++i){
       f[i]=1ll*f[i-1]*2%mod;
       if(i&1) f[i]=(1ll*f[i]+get_C(i-1,(i-1)>>1))%mod;
       else  f[i]=(1ll*f[i]-get_C(i-1,i>>1)+mod)%mod;
   }
   int ans=0,pk=1;
   for(int k=0;k<=n;++k){
       ans=(ans+1ll*get_C(n,k)*pk%mod*f[n-k]%mod*(k+1)%mod)%mod;
       pk=1ll*pk*p%mod;
   }
   int in=qpow(p+2,n);
   in=qpow(in,mod-2);
   ans=1ll*ans*in%mod;
   printf("%d\n",ans);
   return 0;
}

C. 万猪拱塔

奇妙的线段树维护

发现符合要求的子矩阵一定是值域连续的,然后上题解。。。

考虑判定值在区间 [l,r] 内的所有数所在的格子是否形成了一个矩形,记这些
格子的颜色为黑色,其它的格子颜色为白色。

考虑所有的 (n+1)×(m+1)2×2的小正方形 (部分超出边界也算),则所有
黑色格子形成一个矩形,当且仅当恰好有 4 个小正方形内部有 1 个黑色格子,
并且没有任何一个小正方形内部有 3 个黑色格子

从小到大枚举 r ,对每个lr,记 f(l) 表示染黑权值在 [l,r]内的格子后,有多
少小正方形内部有1个或3个黑色格子

可以发现有 f(l)4,f(r)=4 ,于是只需要对每个 l 维护 f(l) 最小值,最小值的
数目和取得最小值的所有 l 之和

每次r 增加1 时,会影响到周边的42×2 的小正方形,在线段树上修改即可

时间复杂度 O(nmlognm)

code

#include <cstring>
#include <cstdio>
#include<algorithm>
#include<unordered_map>
typedef long long ll;
using namespace std;
void swap(int &x,int &y){x^=y;y^=x;x^=y;}
ll min(ll x,ll y){return x<y?x:y;}
ll max(ll x,ll y){return x>y?x:y;}
const int maxn=200005;
const int mod=998244353;
unordered_map<ll,ll>mp[maxn];
struct node{ll cnt,lazy,sum,min;}ret;
int n,m;
struct tree{
    
    node t[maxn<<2|1];

    void built(int x ,int l,int r){
        if(l==r){
            t[x].cnt=1;
            t[x].sum=l;
            return;
        }
        int mid=(l+r)>>1;
        built(x<<1,l,mid);
        built(x<<1|1,mid+1,r);
    }

    void push_up(int x){
        t[x].sum=t[x].cnt=0;
        t[x].min=min(t[x<<1].min,t[x<<1|1].min);
        if(t[x].min==t[x<<1].min)t[x].cnt+=t[x<<1].cnt,t[x].sum+=t[x<<1].sum;
        if(t[x].min==t[x<<1|1].min)t[x].cnt+=t[x<<1|1].cnt,t[x].sum+=t[x<<1|1].sum;
        
    }

    void push_down(int x){
        t[x<<1].lazy+=t[x].lazy;
        t[x<<1|1].lazy+=t[x].lazy;
        t[x<<1].min+=t[x].lazy;
        t[x<<1|1].min+=t[x].lazy;
        t[x].lazy=0;
    }

    void modify(int x,int l,int r,int L,int R,ll z){
        if(L>R)return;
        if(L<=l&&r<=R){
            t[x].lazy+=z;
            t[x].min+=z;
            return;
        }
        int mid=(l+r)>>1;
        if(t[x].lazy)push_down(x);
        if(L<=mid)modify(x<<1,l,mid,L,R,z);
        if(R>mid)modify(x<<1|1,mid+1,r,L,R,z);
        push_up(x);
    }

    void query(int x,int l,int r,int L,int R){
        if(L<=l&&r<=R){
            if(t[x].min==4){
                ret.sum+=t[x].sum;
                ret.cnt+=t[x].cnt;
            }
            return;
        }
        if(t[x].lazy)push_down(x);
        int mid=(l+r)>>1;
        if(L<=mid)query(x<<1,l,mid,L,R);
        if(R>mid)query(x<<1|1,mid+1,r,L,R);
    }
    
    

}T;
struct tpp{int x,y;}tp[maxn];
int ls[25];
void work(int x,int y,int w){
    ll cnt=0,p,k=1;
    ls[++cnt]=mp[x][y];
    ls[++cnt]=mp[x-1][y-1];
    ls[++cnt]=mp[x-1][y];
    ls[++cnt]=mp[x][y-1];
    sort(ls+1,ls+cnt+1);
    p=lower_bound(ls+1,ls+cnt+1,w)-ls;//防止更新非法状态
    for(p;p>=1;--p,k*=-1)T.modify(1,1,n,ls[p-1]+1,ls[p],k);
}
int main(){
     freopen("pig.in","r",stdin);
     freopen("pig.out","w",stdout);
    
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n+1;++i)mp[0][i]=mp[n+1][i]=mp[i][0]=mp[i][n+1]=maxn;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j){
            scanf("%d",&mp[i][j]);
            tp[mp[i][j]].x=i;tp[mp[i][j]].y=j;
        }

    n=n*m;
    T.built(1,1,n);
    
    long long ans=0;
    for(int i=1;i<=n;++i){
        //对受影响的四个矩形进行操作
        work(tp[i].x,tp[i].y,i);
        work(tp[i].x+1,tp[i].y+1,i);
        work(tp[i].x+1,tp[i].y,i);
        work(tp[i].x,tp[i].y+1,i);
        //查询当前贡献
        T.query(1,1,n,1,i);
        ans=(ans+(i+1)*ret.cnt-ret.sum+mod)%mod;
        ret.cnt=ret.sum=0;
    }
    printf("%lld\n",ans);
    return 0;
}

D. 抑郁刀法

不会。这题给我的唯一收获--了解了什么是NPC问题

粘一下题解

考虑对图进行收缩,对于度数为 1的点,可以直接将其删掉,并将最后答案乘
(k1)

对于度数为 2 的点,记它连向的两个点分别为a,b

将这个点删掉,若最后 a,b 之间同色,则有 k1 种情况它与 a,b 都异色,有 1
种情况它与 a,b 都同色

a,b 之间异色,则有 k2 种情况它与 a,b 都异色,有1种情况与a同色,1 种情况与 b 同色

我们为每两个点之间记录f(a,b),g(a,b)分别表示它们同色时答案乘上的系数,异色时答案乘上的系数。

在删掉度数为2的点时更新对应的 f(a,b) 即可

最终得到的图中,每个点度数至少为 3 ,结合 mn+5 可算出 n10,m15

对这个图做状压 dp ,并考虑上 f,g 的系数即可

状压 dp ,每次选择一个未染色的子集进行染色,时间复杂度 O(nm3n)

code

posted @   Chen_jr  阅读(62)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示