奇怪的道路
description
小宇从历史书上了解到一个古老的文明。这个文明在各个方面高度发达,交通方面也不例外。考古学家已经知道,这个文明在全盛时期有n座城市,编号为1⋯n1\cdots n1⋯n。mmm 条道路连接在这些城市之间,每条道路将两个城市连接起来,使得两地的居民可以方便地来往。一对城市之间可能存在多条道路。
据史料记载,这个文明的交通网络满足两个奇怪的特征。首先,这个文明崇拜数字K,所以对于任何一条道路,设它连接的两个城市分别为 uuu 和 vvv,则必定满足 1≤∣u−v∣≤K1\leq |u - v| \leq K1≤∣u−v∣≤K。此外,任何一个城市都与恰好偶数条道路相连(0也被认为是偶数)。不过,由于时间过于久远,具体的交通网络我们已经无法得知了。小宇很好奇这nnn个城市之间究竟有多少种可能的连接方法,于是她向你求助。
方法数可能很大,你只需要输出方法数模100000000710000000071000000007后的结果。
solution
考虑dp,用f[i][j][k][S]f[i][j][k][S]f[i][j][k][S]表示第iii个点,用了jjj条边,iii已经连了1⋯i−k1\cdots i-k1⋯i−k的边,前kkk个点的读书的奇偶性是SSS
那么对于i−k+1i-k+1i−k+1这个点,我们可以选择连一条边,即
f[i][j+1][k][S′]+=f[i][j][k][S]f[i][j+1][k][S^\prime]+=f[i][j][k][S]f[i][j+1][k][S′]+=f[i][j][k][S]
也可以选择不连
f[i][j][k−1][S′]+=f[i][j][k][S]f[i][j][k-1][S^\prime]+=f[i][j][k][S]f[i][j][k−1][S′]+=f[i][j][k][S]
同时还有当距离SSS为kkk的点的度数为偶数时,可以向i+1i+1i+1转移
code
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
const int N=35;
const int inf=2e9;
const int mod=1e9+7;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,m,K;
int f[N][N][N][1<<9];
int main()
{
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
read(n),read(m),read(K);
f[1][0][0][0]=1;
int ss=(1<<K+1)-1;
Rep(i,1,n){
Rep(j,0,m)
Rep(S,0,ss)
_Rep(k,min(i-1,K),1){
if(j<m)f[i][j+1][k][S^(1<<k)^1]+=f[i][j][k][S],f[i][j+1][k][S^(1<<k)^1]%=mod;
f[i][j][k-1][S]+=f[i][j][k][S],f[i][j][k-1][S]%=mod;
}
Rep(j,0,m)if(i<n)
Rep(S,0,ss)
if(!(S&(1<<K))){
f[i+1][j][i>=K?K:i][(S<<1)&ss]+=f[i][j][0][S];
f[i+1][j][i>=K?K:i][(S<<1)&ss]%=mod;
}
}
printf("%d\n",f[n][m][0][0]);
return 0;
}
搬运工
description
小涵向小宇推荐了一款小游戏。
游戏是这样的,在一个n×nn\times nn×n的地图中,有若干个格子上有障碍物。你需要雇佣搬运工,将这些障碍物全部清除。不过每次操作你只能让搬运工将某一行或者某一列的障碍物全部清除。如果你让搬运工清除第i行障碍物,需要付出aia_iai元;如果你让搬运工清除第j列障碍物,需要付出bjb_jbj元。
小涵告诉小宇,必须用尽可能少的次数消除这些障碍物。若有多种方案,则必须花费尽量少的费用。结果小宇想了很久仍然没有闯过第一关,只好向你求助了。
n≤100,ai,bi≤100n\leq 100,a_i,b_i\leq 100n≤100,ai,bi≤100
solution
只有第一问,就是一个裸的二分图匹配
如果第二问不要求“在最少次数的前提下”,那么就是按费用建图跑最小割
所以这题的做法就是把它们结合起来
设Δ=106\Delta=10^6Δ=106,sss向iii连ai+Δa_i+\Deltaai+Δ的边,i+ni+ni+n向ttt连bi+Δb_i+\Deltabi+Δ的边,中间建inf
跑出来的最大流/Δ\DeltaΔ就是第一问,最大流%Δ\DeltaΔ就是第二问
#include <bits/stdc++.h>
using namespace std;
#define int long long
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
const int N=1e5+5;
const int inf=2e12;
const int Q=1e6+5;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,s,t;
int a[N],b[N];
int head[N],cnt;
int dep[N];
int ans;
char str[205][205];
struct Edge{
int to,next,w;
}e[N*20];
void add(int x,int y,int c){
e[++cnt]=(Edge){y,head[x],c},head[x]=cnt;
e[++cnt]=(Edge){x,head[y],0},head[y]=cnt;
}
bool bfs(){
memset(dep,0,sizeof(dep));
queue<int> q;
q.push(s);
dep[s]=1;
while(!q.empty()){
int u=q.front();q.pop();
RepG(i,u){
int v=e[i].to;
if(e[i].w&&!dep[v]){
dep[v]=dep[u]+1;
q.push(v);
if(v==t)return true;
}
}
}
return false;
}
int dfs(int u,int flow){
if(u==t)return flow;
int rest=flow,k;
for(int i=head[u];~i&&rest;i=e[i].next){
int v=e[i].to;
if(e[i].w&&dep[v]==dep[u]+1){
k=dfs(v,min(rest,e[i].w));
if(!k)dep[v]=0;
e[i].w-=k;
e[i^1].w+=k;
rest-=k;
}
}
return flow-rest;
}
void dinic(){
ans=0;
int tmp;
while(bfs())
while(tmp=dfs(s,inf))ans+=tmp;
}
signed main()
{
freopen("worker.in","r",stdin);
freopen("worker.out","w",stdout);
memset(head,-1,sizeof(head)),cnt=1;
scanf("%d",&n);
s=0,t=2*n+1;
Rep(i,1,n)scanf("%s",str[i]+1);
Rep(i,1,n)
Rep(j,1,n)
if(str[i][j]=='*')add(i,j+n,inf);
Rep(i,1,n)read(a[i]);
Rep(i,1,n)read(b[i]);
Rep(i,1,n)add(s,i,a[i]+Q);
Rep(i,1,n)add(i+n,t,b[i]+Q);
dinic();
printf("%lld\n",ans/Q);
printf("%lld\n",ans%Q);
return 0;
}
/*
3
...
.*.
**.
10 5 17
1 8 4
*/
752

被折叠的 条评论
为什么被折叠?



