• AGC026D Histogram Coloring


    link

    题意:

    给定n列的方块,第i列高度$h_i$。现在要把它染成红蓝两色,要求满足:对于任意一个$2 imes 2$的区域,恰有2个蓝色,2个红色。问方案数。

    $nleq 100,h_ileq10^9.$

    题解:

    观察到一个性质:对于同行相邻两个格子,如果颜色相同,那么下一行的颜色必定取反;否则下一行可以取反也可以不取。那么,对于任一行,如果存在相邻两个格子颜色相同,下一行的染色方法唯一;否则存在两种染色方案。(以下所述的“存在/不存在”都是指“存在/不存在相邻两个格子颜色相同”)

    考虑保存两个量:first:存在相邻格子颜色相同情况的方案数;second:不存在的方案数(固定第一个格子的颜色,也就是最终答案需要乘2)。

    如果是一个矩形很容易计算答案。否则定义solve(l,r,lim)表示区间[l,r]比lim高的部分染色方案数,每次对于这段区间把下面整块矩形的部分砍掉,上面部分递归处理。用s0,s1维护上方有方格的列,当前行存在/不存在的方案数,那么可以方便地和上方没有方格的部分合并答案。注意计数过程中一些细节问题。

    时间复杂度$mathcal{O}(n^2)$。

    code:

     1 #include<bits/stdc++.h>
     2 #define rep(i,x,y) for (int i=(x);i<=(y);i++)
     3 #define ll long long
     4 #define inf 1000000001
     5 #define y1 y1___
     6 #define pii pair<int,int>
     7 #define fi first
     8 #define se second
     9 using namespace std;
    10 char gc(){
    11     static char buf[100000],*p1=buf,*p2=buf;
    12     return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
    13 }
    14 #define gc getchar
    15 ll read(){
    16     char ch=gc();ll x=0;int op=1;
    17     for (;!isdigit(ch);ch=gc()) if (ch=='-') op=-1;
    18     for (;isdigit(ch);ch=gc()) x=(x<<1)+(x<<3)+ch-'0';
    19     return x*op;
    20 }
    21 #define N 105
    22 #define mod 1000000007
    23 int ksm(int x,int p){
    24     int ret=1;
    25     for (;p;p>>=1,x=(ll)x*x%mod) if (p&1) ret=(ll)ret*x%mod;
    26     return ret;
    27 }
    28 int n,h[N];
    29 pii solve(int l,int r,int lim){//区间[l,r]比lim高的部分的方案数
    30     int mi=inf,cnt=0;pii ret;//first:存在相邻格子颜色相同情况的方案数;second:不存在的方案数(固定第一个格子的颜色)
    31     rep (i,l,r) if (h[i]<mi) mi=h[i],cnt=1;else if (h[i]==mi) cnt++;
    32     if (cnt==r-l+1){//矩形
    33         ret.fi=(ksm(2,r-l+1)+mod-2)%mod;
    34         ret.se=ksm(2,mi-lim-1);
    35         return ret;
    36     }
    37     int rest=r-l+1,s0=1,s1=1,last=0;//rest:上方没有方格的列数;s0,s1:维护上方有方格的列,当前行存在/不存在的方案数
    38     rep (i,l,r+1)
    39         if (!last&&h[i]>mi) last=i;
    40         else if (last&&(h[i]<=mi||i>r)){
    41             rest-=i-last;
    42             pii tmp=solve(last,i-1,mi);//子问题,递归求解
    43             s0=(ll)s0*(tmp.fi+4ll*tmp.se%mod)%mod;//*4是因为上一行可以取反,当前行亦然,2*2
    44             s1=(ll)s1*(2ll*tmp.se%mod)%mod;
    45             last=0;
    46         }
    47     s0=(s0+mod-s1)%mod;
    48     ret.fi=(ll)s0*ksm(2,rest)%mod;//如果上方方格已经存在,剩下的列随意
    49     ret.fi=(ret.fi+(ll)s1*(ksm(2,rest)+mod-2)%mod)%mod;//否则需要去掉两种不合法的情况
    50     ret.se=(ll)s1*ksm(2,mi-lim-1)%mod;//固定第一个格子(第一行)颜色
    51     return ret;
    52 }
    53 int main(){
    54     n=read();rep (i,1,n) h[i]=read();
    55     if (n==1){//注意特判
    56         printf("%d
    ",ksm(2,h[1]));
    57         exit(0);
    58     }
    59     int ex=1;
    60     rep (i,1,n) if (h[i]>h[i-1]&&h[i]>h[i+1]){
    61         ex=(ll)ex*ksm(2,h[i]-max(h[i-1],h[i+1]))%mod;
    62         h[i]=max(h[i-1],h[i+1]);
    63     }
    64     pii ans=solve(1,n,0);
    65     printf("%d",(ll)ex*(ans.fi+2ll*ans.se%mod)%mod);
    66     return 0;
    67 }
    View Code

    易错:

    n=1的时候需要特判,因为否则的话调用ksm的时候p会变负,导致TLE。

  • 相关阅读:
    Android NDK API Reference
    MySQL和MariaDB的版本对应关系
    Android NFC Host Card Emulation (HCE)
    ndk原生例子
    观测下老外的水平如何
    termius好用的shell终端
    NFC读取卡片ID
    idea Mac格式化代码快捷键
    微软出品自动化神器【Playwright+Java】系列(一) 之 环境搭建及脚本录制
    Mac系统用Maven本地引入jar包报错问题解决
  • 原文地址:https://www.cnblogs.com/bestFy/p/9315702.html
Copyright © 2020-2023  润新知