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 }