T1:
题目大意:有一张有向无环图,第$x$次经过边$i$的代价为$a_ix+b_i$,最多经过$c_i$次,起点为1,$s$个点可作为终点,求走$k$次的最小代价。
我们新建一个汇点,将所有可做为终点的边到汇点连边,那么本题便成为了费用流模型。
贪心策略为:每次走最短路。
证明:路径的顺序是可以改变的,设每次走的路径代价是递增的,如果当前不走最短路,那么以后不可能有一条路能将代价追回,所以当前走最短路一定最优。
但是每次增广代价是不同的,我们只能进行完一次增广之后立即修改边权。
考虑EK,每次用spfa找一条代价最小的增广路,并更新费用即边权,若当前边是正向边,则将正向边权加$a_i$,反向边权减$a_i$,反之将正向边权减$a_i$,反向边权加$a_i$。正向边初始权值为$a_i+b_i$,由于反向边退流退的是上一层的费用,所以初值应赋为$-b_i$。
然后增广k次,若流量不能达到k,输出-1。
spfa复杂度视为$O(NM)$时,时间复杂度$O(NMK)$,但远远达不到。
Code:

1 #include<iostream>
2 #include<cstdio>
3 #include<queue>
4 using namespace std;
5 const int N=1010;
6 const int M=20010;
7 const int inf=1e9+10;
8 int n,m,k,s,nm=1,ans=0;
9 int fi[N],p[N],d[N];
10 bool v[N];
11 struct edge{
12 int v,ne;
13 int l,a,b;
14 }e[M+N<<1];
15 queue<int> q;
16 int read()
17 {
18 int s=0;char c=getchar();
19 while(c<'0'||c>'9') c=getchar();
20 while(c>='0'&&c<='9'){
21 s=(s<<3)+(s<<1)+c-'0';
22 c=getchar();
23 }
24 return s;
25 }
26 void add(int x,int y,int z1,int z2,int z3)
27 {
28 e[++nm].v=y;e[nm].a=z1;e[nm].b=z1+z2;
29 e[nm].l=z3;e[nm].ne=fi[x];fi[x]=nm;
30 e[++nm].v=x;e[nm].a=-z1;e[nm].b=-z2;
31 e[nm].l=0;e[nm].ne=fi[y];fi[y]=nm;
32 }
33 bool spfa()
34 {
35 for(int i=1;i<=n;i++){
36 p[i]=0;d[i]=inf;v[i]=false;
37 }
38 while(!q.empty()) q.pop();
39 d[1]=0;v[1]=true;q.push(1);
40 while(!q.empty()){
41 int x=q.front();q.pop();
42 v[x]=false;
43 for(int i=fi[x];i!=0;i=e[i].ne){
44 int y=e[i].v;
45 if(e[i].l==0) continue;
46 if(d[y]>d[x]+e[i].b){
47 d[y]=d[x]+e[i].b;p[y]=i;
48 if(!v[y]){
49 v[y]=true;q.push(y);
50 }
51 }
52 }
53 }
54 if(d[n]<inf) return true;
55 else return false;
56 }
57 void update()
58 {
59 int x=n;ans+=d[n];
60 while(x!=1){
61 int y=p[x];
62 if((y&1)==0) e[y].b+=e[y].a,e[y^1].b+=e[y^1].a;
63 else e[y].b-=e[y].a,e[y^1].b-=e[y^1].a;
64 e[y].l-=1;e[y^1].l+=1;
65 x=e[y^1].v;
66 }
67 }
68 int main()
69 {
70 scanf("%d%d%d%d",&n,&m,&k,&s);
71 n++;
72 for(int i=1;i<=s;i++){
73 int x=read();
74 add(x,n,0,0,inf);
75 }
76 for(int i=1;i<=m;i++){
77 int x=read(),y=read(),a=read(),b=read(),c=read();
78 add(x,y,a,b,c);
79 }
80 int tot=0;
81 while(tot<k&&spfa()){
82 update();tot++;
83 }
84 if(tot!=k) printf("-1\n");
85 else printf("%d\n",ans);
86 return 0;
87 }
T2:
题目大意:有两串数字x和y,以及n串数字段,求$[x+1,y]$内至少含有n个数字段的数的个数,对$1e9+7$取模。
一个数字段可以被包含多次,重复的数字段也要重复计算。
由区间可以看出此题为数位DP,然后发现字串包含,于是想AC自动机。
然后这道题变为了AC自动机上的数位DP。
按照数位DP方法,我们先求出$[1,y]$中合法解的个数,再减去$[1,x]$中合法解的个数,即为答案。
建出AC自动机,每个点的权值为以该节点为结尾的串的数量,用trie图优化,注意每个节点要继承fail的信息。
设$dp[i][j][k][0/1]$,代表匹配到第i位,在节点j,匹配了k个子串的方案数,1代表有限制,0代表无限制。
对于每个数,有无前缀0与其大小无关,于是我们可以带着前缀0进行DP转移。
设$S$为指向$x$的点集,$w[i]$为节点i的权值,$t[i]$为节点i的类型,$a$为较大的边界,则:
$dp[i][x][j][0]= \sum _{y \in S} dp[i-1][y][j-w[x]][0]+[t[x]<a[i]]* \sum _{y \in S} dp[i-1][y][j-w[x]][1]$
$dp[i][x][j][1]=[t[x]==a[i]]* \sum _{y \in S} dp[i-1][y][j-w[x]][1]$
由于没有考虑前缀0影响,统计答案时只累加长度等于原串的答案即可。
时间复杂度$O(NSK)$,$S$为子串总长。
Code:

1 #include<iostream>
2 #include<cstdio>
3 #include<string>
4 #include<cstring>
5 #include<queue>
6 #define LL long long
7 using namespace std;
8 const LL mod=1e9+7;
9 int n,k,rt=0,cnt=0;
10 string s;
11 int a[2][510];
12 LL dp[510][210][20][4];
13 bool v[510][210][20][4];
14 struct trie{
15 int ch[10],fail;
16 int e;
17 }t[210];
18 queue<int> q,q1,q2,q3,q4;
19 void insert()
20 {
21 int now=rt;
22 for(int i=0;i<s.size();i++){
23 int x=s[i]-'0';
24 if(t[now].ch[x]==0)
25 t[now].ch[x]=++cnt;
26 now=t[now].ch[x];
27 }
28 t[now].e++;
29 }
30 void build()
31 {
32 for(int i=0;i<=9;i++){
33 if(t[rt].ch[i]!=0) q.push(t[rt].ch[i]);
34 }
35 while(!q.empty()){
36 int x=q.front();q.pop();
37 for(int i=0;i<=9;i++){
38 if(t[x].ch[i]!=0){
39 t[t[x].ch[i]].fail=t[t[x].fail].ch[i];
40 t[t[x].ch[i]].e+=t[t[t[x].fail].ch[i]].e;
41 q.push(t[x].ch[i]);
42 }
43 else t[x].ch[i]=t[t[x].fail].ch[i];
44 }
45 }
46 }
47 LL work(int id)
48 {
49 memset(dp,0,sizeof(dp));
50 memset(v,false,sizeof(v));
51 while(!q1.empty()){
52 q1.pop();q2.pop();q3.pop();q4.pop();
53 }
54 dp[0][rt][0][3]=1;v[0][rt][0][3]=true;
55 q1.push(0);q2.push(rt);q3.push(0);q4.push(3);
56 while(!q1.empty()){
57 int x=q1.front(),y=q2.front(),z=q3.front(),op=q4.front();
58 v[x][y][z][op]=false;
59 q1.pop();q2.pop();q3.pop();q4.pop();
60 if(x==a[id][0]) break;
61 for(int i=0;i<=((op&1)==1?a[id][x+1]:9);i++){
62 int yy=t[y].ch[i];int zz=z+t[yy].e;
63 if(zz>k) zz=k;
64 if((op&2)==2&&i==0){
65 if((op&1)==1&&i==a[id][x+1]){
66 dp[x+1][yy][zz][3]=(dp[x+1][yy][zz][3]+dp[x][y][z][op])%mod;
67 if(!v[x+1][yy][zz][3]){
68 q1.push(x+1);q2.push(yy);q3.push(zz);q4.push(3);
69 v[x+1][yy][zz][3]=true;
70 }
71 }
72 else{
73 dp[x+1][yy][zz][2]=(dp[x+1][yy][zz][2]+dp[x][y][z][op])%mod;
74 if(!v[x+1][yy][zz][2]){
75 q1.push(x+1);q2.push(yy);q3.push(zz);q4.push(2);
76 v[x+1][yy][zz][2]=true;
77 }
78 }
79 }
80 else{
81 if((op&1)==1&&i==a[id][x+1]){
82 dp[x+1][yy][zz][1]=(dp[x+1][yy][zz][1]+dp[x][y][z][op])%mod;
83 if(!v[x+1][yy][zz][1]){
84 q1.push(x+1);q2.push(yy);q3.push(zz);q4.push(1);
85 v[x+1][yy][zz][1]=true;
86 }
87 }
88 else{
89 dp[x+1][yy][zz][0]=(dp[x+1][yy][zz][0]+dp[x][y][z][op])%mod;
90 if(!v[x+1][yy][zz][0]){
91 q1.push(x+1);q2.push(yy);q3.push(zz);q4.push(0);
92 v[x+1][yy][zz][0]=true;
93 }
94 }
95 }
96 }
97 }
98 LL ans=0;
99 for(int j=0;j<=cnt;j++){
100 ans=(ans+dp[a[id][0]][j][k][0])%mod;
101 ans=(ans+dp[a[id][0]][j][k][1])%mod;
102 }
103 return ans;
104 }
105 int main()
106 {
107 scanf("%d%d",&n,&k);
108 cin>>s;a[0][0]=s.size();
109 for(int i=1;i<=a[0][0];i++) a[0][i]=s[i-1]-'0';
110 cin>>s;a[1][0]=s.size();
111 for(int i=1;i<=a[1][0];i++) a[1][i]=s[i-1]-'0';
112 for(int i=1;i<=n;i++){
113 cin>>s;insert();
114 }
115 build();
116 LL ans=work(1);
117 ans-=work(0);
118 ans=(ans%mod+mod)%mod;
119 printf("%lld\n",ans);
120 return 0;
121 }
