NOIP模拟5 T2

Posted LO_XJY_VE

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP模拟5 T2相关的知识,希望对你有一定的参考价值。

题面:求出满足以下条件的 n*m 的 01 矩阵个数:
    (1)第 i 行第 1~li 列恰好有 1 个 1 (li+1到ri-1不能放1)

    (2)第 i 行第 ri~m 列恰好有 1 个 1。
    (3)每列至多有 1 个 1

    对于 20%的数据,n,m<=12。
    对于 40%的数据,n,m<=50。
    对于 70%的数据,n,m<=300。
    对于 100%的数据,n,m<=3000,1<=li<ri<=m。

(考试前一天晚上玩过头了,整场考试直接划水,本题打了个状压,想不出来怎么优化就跳了)

 通过对题目的初步分析 本题是一类计数问题

  那么考虑这类问题解法通常为应用计数类DP或数学模型求解(本题显然DP色彩很浓厚)

然而 无论应用实际意义,数学模型都无法推出完整的式子 那么我们就需要思考DP类做法(事实上DP也是更为通用的做法)

  常规思考 问题状态有行,列,点 很容易想出状压DP的做法f[i][j]代表进行到第i行同时第i行状态为j

然而根据数据范围状压只能拿到20pts,显然不是正解 问题在于对于较大的m无法记录其状态 这样 直接否定了对行进行状压的过程

  思考问题在于枚举同一行点的状态的时间空间复杂度无法承受 那么为我们就要尽量降低这种复杂度

一种很显然的想法是既然我们不能记录同一行点的状态 那么缩小问题 记录单个点的状态的代价显然是能接受的

  那么思路逐渐清晰 我们需要考虑问题中行或列与节点的关系

一种常规DP设计手法是根据目标设计状态,阶段与决策 那么应用到本题上 目标是求n*m矩阵在一定条件下合法放置1的不同矩阵数

倒推分解问题 得 问题实际上是行或列放置1方案数的转移

  因此 设计状态f[i][j]表示已经进行到i列 其中有j列中的1放置在右区间,那么我们最终的目标就是f[m][n];

(这道题DP设计很新颖,思维量较高,然而其本质仍然是计数类DP的设计理念:通过精确划分使得状态各决策间满足加法原理 子状态间满足乘法原理(不重不漏))

显然本题状态划分是基于左右区间的互斥性 也就是说通过分别计算当前状态左右区间合法方案数来推出当前状态合法方案数

  很容易想到对于右区间 f[i][j] = f[i - 1][j] + f[i - 1] * (r[i] - (j - 1))  <1>  //其中r[i]代表右区间在i列及其以右的区间总数 l[i]同理

而有趣的是本题左右区间计算并不是完全独立的 相反 左区间的计算正是基于右区间已知的情况下进行的 (这为我们进行DP设计,具体到状态转移提供了新思路)

公式并不难推 f[i][j] = A(i - j - l[i - 1],l[i] - l[i - 1])  <2> ;最终根据乘法原理 状态f[i][j] = <1> * <2>;

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define I int
 4 #define LL long long
 5 #define C char
 6 #define RE register 
 7 #define L inline
 8 const I mod = 998244353;
 9 const I MAXN = 3050;
10 I n,m,l[MAXN],r[MAXN],j[MAXN],y[MAXN],f[MAXN][MAXN]; 
11 L I read(); L LL qpow(I,I); L LL A(I,I); L I getmin(I,I);
12 signed main(){
13     n = read(); m = read();j[0] = 1;f[0][0] = 1;
14     for(RE I i(1);i <= n; ++ i) ++l[read()],++r[read()];
15     for(RE I i(1);i <= m; ++ i) l[i] += l[i - 1],r[i] += r[i - 1];
16     for(RE I i(1);i <= m; ++ i)   j[i] = 1ll * j[i - 1] * i % mod;
17     y[m] = qpow(j[m],mod - 2);
18     for(RE I i(m); i ; -- i)    y[i - 1] = 1ll * y[i] * i % mod;
19     for(RE I i(1);i <= m; ++ i){
20       f[i][0] = 1ll * f[i - 1][0] * A(i - l[i - 1],l[i] - l[i - 1]) % mod;
21         for(RE I j(1);j <= getmin(n,i); ++ j)
22           f[i][j] = (f[i - 1][j] + 1ll * f[i - 1][j - 1] * (r[i] - j + 1)%mod) * A(i - j - l[i - 1],l[i] - l[i - 1]) % mod; 
23     }printf("%d\\n",f[m][n]);
24 }
25 L I read(){  RE I x(0); RE C z = getchar(); while(!isdigit(z)) z = getchar(); while(isdigit(z)) x = (x << 3) + (x << 1) + (z ^ 48),z = getchar(); return x;  }
26 L LL qpow(I a,I b){
27     LL res(1),base(a);
28     while(b){
29         if(b & 1)   res = 1ll * res * base % mod;
30         base = 1ll * base * base % mod;
31         b >>= 1;
32     }return res;     }
33 L LL A(I n,I m){  if(m > n) return 0; return 1ll * j[n] * y[n - m] % mod;  }
34 L I getmin(I a,I b){  return a < b ? a : b;  }
View Code

 

总的来说本题真的是DP中一道精彩的问题 对于DP状态设计,决策都提供了较为新颖的思路 建议反复咀嚼

 

以上是关于NOIP模拟5 T2的主要内容,如果未能解决你的问题,请参考以下文章

2017 9 11 noip模拟赛T2

数列 题解(NOIP模拟T2)

NOIP2017模拟赛 senior 6.29 T2 小T的钢琴(piano)

20161022 NOIP模拟赛 T2 解题报告

洛谷 U361 序列操作(NOIP模拟赛T2)

老司机的奇怪noip模拟T2-huangyueying