B - Symmetric Matrix
思路:将矩阵转换成图的形式,然后推公式。
#include#define LL long long#define fi first#define se second#define mk make_pair#define pii pair #define y1 skldjfskldjg#define y2 skldfjsklejg using namespace std; const int N = 1e5 + 7;const int M = 1e5 + 7;const int inf = 0x3f3f3f3f;const LL INF = 0x3f3f3f3f3f3f3f3f; LL dp[N], sum[N];int mod, n;LL fastPow(LL a, LL b) { LL ans = 1; while(b) { if(b & 1) ans = ans * a % mod; a = a * a % mod; b >>= 1; } return ans;} void init() { dp[1] = 0; dp[2] = 1; dp[0] = 1; for(int i = 3; i <= n; i++) { sum[i] = (i - 1) * sum[i - 1] % mod + (1ll * (i - 1) * (i - 2) / 2) % mod * dp[i - 3] % mod; if(sum[i] >= mod) sum[i] -= mod; dp[i] = (i - 1) * dp[i - 2] % mod + sum[i]; if(dp[i] >= mod) dp[i] -= mod; }} int main() { while(scanf("%d%d", &n, &mod) != EOF) { init(); printf("%lld\n", dp[n]); } return 0;} /*3 1000000000100000 1000000000 507109376*/
E - Removal
题目大意:给你 n 个数, 每个数在1 - k 之间,现在让你删除 m 个数,问你最后有多少种序列。
思路:存在重复的问题,对于同一个序列我们只记录最靠前的一个,我们用nx[ i ][ j ]表示 i 后边第一个j 的位置。
可以得到dp方程: 每一个dp[ i ][ j ] 可以转移到 dp[ nx[ i ][ k ] ][ j + nx[ i ][ k ] - i - 1]
#include#define LL long long#define fi first#define se second#define mk make_pair#define pii pair #define y1 skldjfskldjg#define y2 skldfjsklejgusing namespace std;const int N = 1e5 + 7;const int M = 1e5 + 7;const int inf = 0x3f3f3f3f;const LL INF = 0x3f3f3f3f3f3f3f3f;const int mod = 1e9 +7;int n, m, up, a[N], mp[11], nx[N][11], dp[N][11];void init() { memset(mp, 0, sizeof(mp)); for(int i = 0; i <= n; i++) for(int j = 0; j <= 10; j++) dp[i][j] = nx[i][j] = 0;}void add(int &a, int b) { a += b; if(a >= mod) a -= mod;}int main() { while(scanf("%d%d%d", &n, &m, &up) != EOF) { init(); for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); } for(int i = n; i >= 0; i--) { for(int j = 1; j <= up; j++) { nx[i][j] = mp[j]; } mp[a[i]] = i; } dp[0][0] = 1; for(int i = 0; i < n; i++) { for(int j = 0; j <= m; j++) { if(!dp[i][j]) continue; for(int k = 1; k <= up; k++) { int pos = nx[i][k]; if(pos && j + pos - i - 1 <= m) { add(dp[pos][j + pos - i - 1], dp[i][j]); } } } } int ans = 0; for(int i = 1; i <= n; i++) { int j = n - i; if(j <= m) add(ans, dp[i][m - j]); } printf("%d\n", ans); } return 0;}/*3 2 21 2 14 2 21 2 1 2*/
I - Substring
题目大意:给你一个由a, b, c三种字符组成的字符串,两个子串要是形式一样就看成一种子串,比如 aba, aca, bab, bcb, cac, cbc是一种,
aaa, bbb, ccc 是一种。
思路:将 3! 种形式的字符串接在一起形成一个新的串, 然后统计这个串有多少不同的子串, 设单一字符子串的数量为cnt1,
非单一字符子串的数量为 cnt2,所以答案为(cnt2 + 2 * cnt1) / 6
统计有多少不同子串可以用后缀数组求。
#include#define LL long longusing namespace std;const int N = 3e5 + 7;char s[N], str[N];int sa[N], t[N], t2[N], c[N], rk[N], height[N], id[N], b[N], d[N], n, tot;void buildSa(char *s, int n, int m) { int i, j = 0, k = 0, *x = t, *y = t2; for(i = 0; i < m; i++) c[i] = 0; for(i = 0; i < n; i++) c[x[i] = s[i]]++; for(i = 1; i < m; i++) c[i] += c[i - 1]; for(i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i; for(int k = 1; k <= n; k <<= 1) { int p = 0; for(i = n - k; i < n; i++) y[p++] = i; for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i] - k; for(i = 0; i < m; i++) c[i] = 0; for(i = 0; i < n; i++) c[x[y[i]]]++; for(i = 1; i < m; i++) c[i] += c[i - 1]; for(i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i]; swap(x, y); p = 1; x[sa[0]] = 0; for(int i = 1; i < n; i++) { if(y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + k] == y[sa[i] + k]) x[sa[i]] = p - 1; else x[sa[i]] = p++; } if(p >= n) break; m = p; } for(i = 1; i < n; i++) rk[sa[i]] = i; for(i = 0; i < n - 1; i++) { if(k) k--; j = sa[rk[i] - 1]; while(s[i + k] == s[j + k]) k++; height[rk[i]] = k; }}void init() { b[n] = 0; tot = 0; for(int i = n - 1; i >= 0; i--) { if(s[i] == s[i + 1]) b[i] = b[i + 1] + 1; else b[i] = 1; for(int j = 1; j <= 5; j++) b[j * (n + 1) + i] = b[i]; } id[0] = 0; id[1] = 1; id[2] = 2; char ch = 'A'; do { for(int i = 0; i < n; i++) { str[tot++] = id[s[i] - 'a'] + 'a'; } str[tot++] = ch++; } while(next_permutation(id, id + 3)); str[tot] = '\0'; int pos = -1; for(int i = tot - 1; i >= 0; i--) { if(b[i] == 0) pos = i; d[i] = pos; }}int main() { while(scanf("%d", &n) != EOF) { scanf("%s", s); init();// puts("");// puts(str);// puts("\n"); buildSa(str, tot + 1, 180); LL ans = 0; for(int i = 1; i <= tot; i++) {// puts(str + sa[i]); int lcp = height[i]; int last = d[sa[i]]; int cnt = b[sa[i]]; LL ret = 0; if(sa[i] == last || lcp >= last - sa[i]) continue; if(cnt > lcp) { ret += 2 * (cnt - lcp); ret += last - sa[i] - cnt; } else { ret += last - sa[i] - lcp; }// printf("%lld %d %d\n", ret, sa[i], last); ans += ret; } printf("%lld\n", ans / 6); } return 0;}/*4abab4abaa*/