Sol
模拟最大流的一般套路就是求最小割。
题目保证了 \(u<v\),所以我们可以得到如下暴力:
设 \(f_{i,S}\) 表示前 \(i\) 个点,从 \(1\) 能到集合 \(S\) 中的点,割掉的最小边权。那么转移有:
\[f_{i,S} \to f_{i+1,S \cup \{i+1\}}
\]
\[f_{i,S} + \sum_{j \in S}w_{j,i+1} \to f_{i+1,S}
\]
其中 \(w_{i,j}\) 代表 \(u=i,v=j\) 的总容量,如果没有边即为 \(0\),应该好理解吧。
这样是 \(O(n^2 2^n)\) 的,需要优化。
我们发现我们还有一个最重要的性质没用,\(v-u \le k\),而且 \(k\) 很小。
此时你发现我们上面第二种转移枚举的 \(j\) 如果满足 \(j < i-k+1\),那么 \(w_{j,i+1}\) 一定为 \(0\)。
那么我们就不用维护 \(1\) 是否能到编号小于 \(i-k+1\) 的点,此时我们的 \(S\) 只维护了 \([i-k+1,i]\) 内的点,那么时间复杂度就降为了 \(O(n k 2^k)\),可以通过此题。
Code
#include<bits/extc++.h>
using namespace std;
#define int long long const int N=8e4+5;
int n,m,k,f[1<<7],tmp[1<<7];
__gnu_pbds::gp_hash_table<int,int>w;inline int hs(int x,int y)
{return x*(n+1)+y;
}inline void ckmin(int &x,int y){if(y<x)x=y;}signed main()
{ios::sync_with_stdio(false);cin.tie(0);cin>>n>>m>>k;while(m--){int x,y,val;cin>>x>>y>>val;w[hs(x,y)]+=val;}memset(f,0x3f,sizeof f);f[1<<(k-1)]=0;for(int j=1;j<n;j++){memset(tmp,0x3f,sizeof tmp);for(int i=0;i<(1<<k);i++){if(f[i]>2e9)continue;ckmin(tmp[(i>>1)|(1<<(k-1))],f[i]);int val=0,lt=j-k+1;for(int p=0;p<k;p++)if((i>>p)&1)val+=w[hs(lt+p,j+1)];ckmin(tmp[i>>1],f[i]+val);}memcpy(f,tmp,sizeof tmp);}int ans=0x3f3f3f3f3f3f3f3f;for(int i=0;i<(1<<(k-1));i++)ckmin(ans,f[i]);cout<<ans<<"\n";return 0;
}