题意:给n!个n的排列,按字典序从小到大连成一条序列,例如3的情况为:[1,2,3, 1,3,2, 2,1,3 ,2,3,1 ,3,1,2 ,3,2,1],问其中长度为n,且和为sum=n*(n+1)/2的序列有多少个?
思路:我们考虑一下next_perumation函数产生字典序递增的全排列的过程:
假设某一个序列长度为n,最长的递减的后缀长度k,那么它的下一个排列是这样产生的:选取序列第n-k个数,与后k个数中比第n - k个数大的最小的数交换,然后将后k个数按从小到大排序。
例如序列1,2,5,4,3的下一个排列为1,3,2,4,5。我们观察发现:这种时候1,2,(5,4,3,1,3,)2,4,5不满足和为sum了,因为在产生下一个排列的过程中,第n-k个位置的数被替换了。
也就是说,假设一个序列存在长度为k的递减后缀,那么这个后缀不能产生一个长度为sum的序列。例如,1,2,(5,4,3,1,3,)2,4,5不行,但是1,(2,5,4,3,1,)3,2,4,5可以。
所以,我们的任务是找出每个长度为k的递减后缀有多少个?应该为C(n,n-k)*(n-k)!=A(n,n-k)=n!/k!个。因为只要选了前面n-k个数,后面长度为k的递减的序列是固定的,所以我们只需要选n-k个数全排列就行了。
我们可以得到最终的答案了:一共有n*n!-(n-1)个序列,要减去( ∑(k from 1 to n-1) n!/k! )- (n-1)个。
为什么要减去n-1个呢?我们来看最后一个排列(假设n为5)5,4,3,2,1 。5之后的序列不存在,所以要从总的序列数中减去。而这(n-1)个不存在的序列恰好会被判定为不满足题意,也应该减去。
所以总的来说,答案应该是:(所有的序列-不存在的序列)-(不满足的序列-不存在的序列)。我们可以把答案写的更优雅一点:ans=n*n!-∑(k from 1 to n-1) n!/k!。

#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<sstream>
#include<string>
#include<vector>
#include<cstdio>
#include<ctime>
#include<bitset>
#include<algorithm>
#include<string.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
#define lson l , mid , rt << 1
#define rson mid + 1 , r , rt << 1 | 1
ll read()
{
ll x = 0, f = 1; char ch = getchar();
while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
const int maxn = 1000010;
const ll mod = 998244353;
ll s[maxn], f[maxn];
int main() {
ll n;
scanf("%lld", &n);
f[0] = 1, s[n] = 1;
for (ll i = 1; i <= n; i++) {
f[i] = (f[i - 1] * i) % mod;
}
for (ll i = n - 1; i >= 1; i--) {
s[i] = (s[i + 1] * (i + 1)) % mod;
}
ll ans = (n*(f[n])) % mod;
for (ll i = 1; i <= n - 1; i++) {
ans = (ans - s[i] + mod) % mod;
}
cout << ans << endl;
return 0;
}
F POJ 3691
AC自动机+DP,模板题
题意:给出n个带病毒的DNA与一个将要修改的DNA,要求修改后不能与任何一个带病毒的DNA相匹配,问最少修改次数。修改一次即为改动一个字符(可以为A,G,C,T)。
思路:考虑dp,用dp[i][j]表示当前已经判断到了待修改DNA的第i位,且在AC自动机上已经成功地匹配到了第j号点。
我们枚举将要把待修改DNA的第i为修改成的字符k,显然,如果k与原来的第i+1位相同,则不用修改。
这道题的关键是如何在修改DNA后,在AC自动机上找到下一次匹配的位置,只有找到这个位置,我们才能成功地进行状态转移。
用一个数组next,next[j][k]表示当前在AC自动机上的j号点,将下一位修改成k后,下一次在AC自动机上匹配的点的编号。我们先暂时不讨论转移后是否是带病毒节点,那么:
1.如果当前节点有字符为k的后继节点,直接将next[j][k]赋值成它即可。
2.否则,就到当前节点的fail节点去找
3.如果当前节点的fail节点是一个“危险节点”,则将当前节点也标记为危险节点(它能匹配上病毒串)

#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<sstream>
#include<string>
#include<vector>
#include<cstdio>
#include<ctime>
#include<bitset>
#include<algorithm>
#include<string.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
#define lson l , mid , rt << 1
#define rson mid + 1 , r , rt << 1 | 1
ll read()
{
ll x = 0, f = 1; char ch = getchar();
while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
const int maxn = 1010;
const int M = 4;
int m, n, T, t, x, y, u;
int ch[maxn][4];
int v[maxn];
int f[maxn], last[maxn], num;
char str[maxn];
int d[maxn][maxn];
void clear()//Trie树初始化
{
memset(d, -1, sizeof(d));
num = 1;
memset(ch[0], 0, sizeof(ch[0]));
memset(v, 0, sizeof(v));
memset(last, 0, sizeof(last));
}
int idx(char c)
{
switch (c)
{
case 'A':
return 0;
case 'C':
return 1;
case 'G':
return 2;
case 'T':
return 3;
}
return 0;
}
void insert(char str[], int value)//建Trie树
{
int len = strlen(str);
int u = 0;
for (int i = 0; i < len; ++i)
{
int c = idx(str[i]);
if (!ch[u][c])//保存的是结点坐标
{
memset(ch[num], 0, sizeof(ch[num]));
ch[u][c] = num++;//
}
u = ch[u][c];
}
v[u] = value;
}
void getac()
{
queue<int> q;//保存的节点下标
f[0] = 0;
for (int c = 0; c < M; ++c)
{
int u = ch[0][c];
if (u)//不需要优化的else
{
q.push(u);
f[u] = 0;
last[u] = v[u];//WA,可能有长度为1的串
}
}
while (!q.empty())
{
int r = q.front();
q.pop();
for (int c = 0; c < M; ++c)
{
int u = ch[r][c];
if (u)
{
q.push(u);
int s = f[r];
f[u] = ch[s][c];
last[u] = (v[u] || last[f[u]]);//改
}
else //重要优化
ch[r][c] = ch[f[r]][c];
}
}
}
int dp(int u, int k)
{
if (k == n)return 0;
int &ans = d[u][k];
if (ans != -1)return ans;
ans = inf;
for (int i = 0; i < 4; i++)
{
int c = ch[u][i];
if (last[c] == 0)
{
ans = min(ans, dp(c, k + 1) + (idx(str[k]) != i ? 1 : 0));
}
}
return ans;
}
int main()
{
int ncase = 0;
while (scanf("%d", &m) == 1 && m)
{
clear();
while (m--)
{
scanf("%s", str);
insert(str, 1);
}
getac();
scanf("%s", str);
n = strlen(str);
printf("Case %d: %d\n", ++ncase, dp(0, 0) == inf ? -1 : dp(0, 0));
}
return 0;
}

#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<sstream>
#include<string>
#include<vector>
#include<cstdio>
#include<ctime>
#include<bitset>
#include<algorithm>
#include<string.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
#define lson l , mid , rt << 1
#define rson mid + 1 , r , rt << 1 | 1
ll read()
{
ll x = 0, f = 1; char ch = getchar();
while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int nextt[1010][4], trie[1010][4], val[1010], ncnt;
int idx(char c) {
if (c == 'A') return 0;
if (c == 'G') return 1;
if (c == 'C') return 2;
return 3;
}
void init() { memset(trie[0], 0, sizeof(trie[0])), memset(nextt[0], 0, sizeof(nextt[0])), memset(val, 0, sizeof(val)), ncnt = 0; }
void insert(char s[], int len) {
int now = 0;
for (int i = 1; i <= len; i++) {
int c = idx(s[i]);
if (!trie[now][c]) {
now = trie[now][c] = ++ncnt;
memset(trie[now], 0, sizeof(trie[now]));
}
else now = trie[now][c];
}
val[now] = 1;
}
queue<int> q;
int fail[1010];
void bfs() {
while (!q.empty()) q.pop();
for (int i = 0; i < 4; i++)
if (trie[0][i]) q.push(trie[0][i]), fail[trie[0][i]] = 0, nextt[0][i] = trie[0][i];
else nextt[0][i] = 0;
while (!q.empty()) {
int now = q.front(); q.pop();
for (int i = 0; i < 4; i++) {
if (trie[now][i]) {
int f = fail[now];
while (f && !trie[f][i]) f = fail[f];
int son = trie[now][i];
fail[son] = trie[f][i], q.push(son);
nextt[now][i] = trie[now][i];
val[son] |= val[fail[son]];
}
else nextt[now][i] = nextt[fail[now]][i];
}
}
}
int dp[1010][1010], n;
int Dp(char s[], int n) {
memset(dp, 1, sizeof(dp));
dp[0][0] = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j <= ncnt; j++) {
for (int k = 0; k < 4; k++)
if (!val[nextt[j][k]])
dp[i + 1][nextt[j][k]] = min(dp[i + 1][nextt[j][k]], dp[i][j] + (idx(s[i + 1]) != k));
}
int ans = 0x3f3f3f3f;
for (int j = 0; j <= ncnt; j++) if (!val[j]) ans = min(ans, dp[n][j]);
return ans < 1e7 ? ans : -1;
}
char s[1010];
int main() {
int n, cas = 0;
while (~scanf("%d", &n) && n) {
init();
for (int i = 1; i <= n; i++) scanf("%s", s + 1), insert(s, strlen(s + 1));
bfs();
scanf("%s", s + 1);
printf("Case %d: %d\n", ++cas, Dp(s, strlen(s + 1)));
}
}
来源:https://www.cnblogs.com/fengzhongzhuifeng/p/12273223.html
