HihoCoder - 1629 Graph (回滚莫队+并查集按秩合并)
题目:https://vjudge.net/contest/419697#problem/C
遇到了回滚莫队。
https://blog.csdn.net/a54665sdgf/article/details/82501086
开拓了眼界。
并查集随机删除是不行的,但是可以按顺序撤销,回滚莫队恰好可以支持这种操作。
[hihocoder 1635] Colored Nodes(思维+基环树)
https://blog.csdn.net/alan_cty/article/details/78713865
发现,虽然一轮之后,每个点的颜色不知道,但是知道这个点的颜色是从哪个点继承的
然后继承关系是一个基环树
只有环上的颜色最后F[i]值不为0
且环上每个颜色最后答案一样。
然后做一下就行
关键在于发现:颜色不知道,但是知道这个点的颜色是从哪个点继承的,然后转化为基环树问题。
D - Rikka with Subsequences
https://blog.csdn.net/wxh010910/article/details/84950709
(全网仅有的wxh的题解。。。。)
cnt^3怎么处理?
套路:用组合意义拆开。
问题转化成为,三个相同的串,从每个串中分别选出一个子序列,这三个子序列相同的方案数。(方案不一样,当且仅当存在从某个串中选出来的子序列的位置不一样)
但是还是很不好写。。。因为涉及到邻接矩阵的限制的问题。
(此处省略理解代码1.5h)
统计i,j,k位置结尾的合法子序列,必须要考虑上一个是不是能接上去,但是复杂度太高不能再枚举了。
所以先固定i,就知道了a[i],也就是b[j],c[k]必须等于a[i],否则没有意义。
很自然地设sum[j][k]表示,所有a中选<i的,b中选<j的,c中选<k的,且能直接拼在a[i]后面的三个位置作为结尾的合法子序列的方案数(显然这三个位置的字符相同)。
到了一个新的i,那么sum就必须全部重新维护(因为a[i]变了),
而sum[j][k]必然包含了从第二个串选1,2,...j-1的,第三个串选1,2,...,k-1位置的,按照二维前缀和的思想,sum[j][k]=sum[j-1][k]+sum[j][k-1]-sum[j-1][k-1]+val
考虑val是什么
就是以所有(<i,j-1,k-1)结尾的且能拼到i后面的子序列的方案数(要保证三个位置字符相同)
设这个东西为dp[j][k](i维省了),那么dp[j][k]可以在之前几轮i的时候,枚举到a[i]==b[j]==c[k]的(i,j,k)的时候更新到
只要我们先更新sum,再求i层的贡献,再更新dp[j][k]就行了。
代码(from:wxh)
#include <bits/stdc++.h> using namespace std; const int N = 234; const int md = 1e9 + 7; int n, a[N], dp[N][N], sum[N][N]; char board[N][N]; inline void add(int &x, int y) { x += y; if (x >= md) { x -= md; } } inline void sub(int &x, int y) { x -= y; if (x < 0) { x += md; } } int main() { int tt; scanf("%d", &tt); while (tt--) { scanf("%d", &n); for (int i = 0; i < n; ++i) { scanf("%d", &a[i]); --a[i]; } for (int i = 0; i < n; ++i) { scanf("%s", board[i]); } for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { dp[i][j] = 0; } } int answer = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { for (int k = 0; k < n; ++k) { sum[j + 1][k + 1] = board[a[j]][a[i]] == '1' ? dp[j][k] : 0; add(sum[j + 1][k + 1], sum[j + 1][k]); add(sum[j + 1][k + 1], sum[j][k + 1]); sub(sum[j + 1][k + 1], sum[j][k]); } } for (int j = 0; j < n; ++j) { for (int k = 0; k < n; ++k) { if (a[i] == a[j] && a[i] == a[k]) { int ways = 1; add(ways, sum[j][k]); add(answer, ways); add(dp[j][k], ways); } } } } printf("%d\n", answer); } return 0; }