YACS 2022年9月月赛 甲组 T3 123型序列 题解

题目链接

前几天都在搞比赛,今天终于有空来写其他的题解了

那就写点甲组的装 $b$

看了下榜单,最简单的就是 $T3$ 了,我先写个 $T3$ 的题解吧!

这毕竟是一道甲组的题目,于是时间过去了 $30$ 分钟...

一眼就能看出来是一个 $DP$ ,但是状态转移方程和状态设计该怎么弄呢?

首先先来讲一下暴力做法:

暴力

很简单,枚举填什么即可,只能得 $30$ 分

我们可以考虑优化,优化方法似乎只有一种:剪枝

意思就是如果我发现当前第i个地方我填的数字已经不符合要求了,那么就可以直接 $return$ 掉了

怎么判断不符合要求呢?

首先我们需要把这 $m$ 个要求整理一下,为了判断方便,我们建一个 $vector$

 $v[i]$ 存的就是以 $i$ 结尾的区间的要求

当我们填完i时,只需要检查 $v[i]$ 这里面的要求是否符合即可

怎么判断要求是否符合呢?

因为需要知道任意区间内有几种数字,所以我们可以多加三个变量 $a,b,c$

分别表示 $1$ 最后一次填是在哪里,$2$ 最后一次填是在哪里还有 $3$ 最后一次填是在哪里

发现了不合法的就可以直接 $return$  了

但是剪枝依旧没多大用

DP

如果你能想到上面的剪枝优化的话,就应该能够想到这个正解了

如果想要转移,那么我们需要知道任意区间内出现了多少个数字

一眼看下去时间复杂度应该是 $O(n^3)$

那么就可以设计状态了

我们设 $f[i][j][k][l]$ 为当前已经填完了第$i$位且满足要求,$1,2,3$ 这三个数字最后一次出现的位置分别是 $j,k,l$

但是时间复杂度是 $O(n^4)$ 会炸咋办?

不难发现,$j,k,l$ 中的一个一定是 $i$

所以我们改一下状态,设 $f[i][j][k]$ 为将 $1,2,3$ 各自最晚出现位置从大到小排序后为 $i,j,k$,$0$ 代表没有出现

状态转移方程很好想,枚举 $1,2,3$ 分别填在哪一位并且判断一下是否合法即可

另外,如果考虑 $f[i][j][k]$ 是哪几项相加,我们需要在算的时候更新一个 $sum$ 数组

因为我太懒了,所以我们采用前推后的写法,就是考虑 $f[i][j][k]$ 会对哪些东西做贡献

那么第 $i+1$ 项就有三种和第i项有关的填法,可以填和 $a[i]$ 一样的

也可以填和 $a[j]$  一样的或者 $a[k]$ 一样的,就是 $1,2$  和 $3$  

填 $a[i]$ 时:会从 $f[i][j][k]$ 转到 $f[i+1][j][k]$,因为最后填的数字就和上一个一样

填 $a[j]$ 时:从 $f[i][j][k]$ 到 $f[i][i+1][k]$,最后一个填 $a[j]$ 的是当前,但是需要注意下大小排序,所以变成了 $f[i+1][i][k]$

填 $a[k]$ 时:从 $f[i][j][k]$ 到 $f[i+1][i][j]$

代码很好写,待会儿再更新,主要是再判断一下是否符合要求就可以了,

还有, $vector$ 要用 $pair$ 存,不然要多开一维,初始 $f[1][0][0] = 3$

代码来啦!

 1 #include <vector>
 2 #include <iostream>
 3 #define int long long
 4 using namespace std;
 5 int n,m,ans;
 6 int a[310],f[310][310][310];
 7 vector<pair<int,int> > v[310];
 8 bool isleg(int a,int b,int c)
 9 {
10     for(int i = 0;i < v[a].size();i ++)
11     {
12         int l = v[a][i].first;
13         int cnt1 = v[a][i].second,cnt2 = 1;
14         cnt2 += (b >= l) + (c >= l);
15         if(cnt2 != cnt1) return false;
16     }
17     return true;
18 }
19 signed main()
20 {
21     scanf("%lld%lld",&n,&m);
22     for (int i = 1;i <= m;i++)
23     {
24         int l,r,cnt;
25         scanf("%lld%lld%lld",&l,&r,&cnt);
26         v[r].push_back(make_pair(l,cnt));
27     }
28     f[1][0][0] = 3;
29     for(int i = 1;i <= n;i ++)
30     {
31         for(int j = 0;j < i;j ++)
32         {
33             for(int k = 0;k < max(1LL,j);k ++)
34             {
35                 if(!isleg(i,j,k)) continue;
36                 f[i + 1][j][k] = (f[i + 1][j][k] + f[i][j][k]) % 1000000007;
37                 f[i + 1][i][j] = (f[i + 1][i][j] + f[i][j][k]) % 1000000007;
38                 f[i + 1][i][k] = (f[i + 1][i][k] + f[i][j][k]) % 1000000007;
39                 if(i == n) ans = (f[i][j][k] + ans) % 1000000007;
40             }
41         }
42     }
43     printf("%lld\n", ans);
44     return 0;
45 }
posted @ 2022-10-02 12:21  Xy_top  阅读(144)  评论(0)    收藏  举报