2020.8.7集训

奇怪的道路

description
小宇从历史书上了解到一个古老的文明。这个文明在各个方面高度发达,交通方面也不例外。考古学家已经知道,这个文明在全盛时期有n座城市,编号为 1 ⋯ n 1\cdots n 1n m m m 条道路连接在这些城市之间,每条道路将两个城市连接起来,使得两地的居民可以方便地来往。一对城市之间可能存在多条道路。
据史料记载,这个文明的交通网络满足两个奇怪的特征。首先,这个文明崇拜数字K,所以对于任何一条道路,设它连接的两个城市分别为 u u u v v v,则必定满足 1 ≤ ∣ u − v ∣ ≤ K 1\leq |u - v| \leq K 1uvK。此外,任何一个城市都与恰好偶数条道路相连(0也被认为是偶数)。不过,由于时间过于久远,具体的交通网络我们已经无法得知了。小宇很好奇这 n n n个城市之间究竟有多少种可能的连接方法,于是她向你求助。
方法数可能很大,你只需要输出方法数模 1000000007 1000000007 1000000007后的结果。

solution

考虑dp,用 f [ i ] [ j ] [ k ] [ S ] f[i][j][k][S] f[i][j][k][S]表示第 i i i个点,用了 j j j条边, i i i已经连了 1 ⋯ i − k 1\cdots i-k 1ik的边,前 k k k个点的读书的奇偶性是 S S S

那么对于 i − k + 1 i-k+1 ik+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][k1][S]+=f[i][j][k][S]

同时还有当距离 S S S k k k的点的度数为偶数时,可以向 i + 1 i+1 i+1转移

code

#include 
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 × n n\times n n×n的地图中,有若干个格子上有障碍物。你需要雇佣搬运工,将这些障碍物全部清除。不过每次操作你只能让搬运工将某一行或者某一列的障碍物全部清除。如果你让搬运工清除第i行障碍物,需要付出 a i a_i ai元;如果你让搬运工清除第j列障碍物,需要付出 b j b_j bj元。
小涵告诉小宇,必须用尽可能少的次数消除这些障碍物。若有多种方案,则必须花费尽量少的费用。结果小宇想了很久仍然没有闯过第一关,只好向你求助了。

n ≤ 100 , a i , b i ≤ 100 n\leq 100,a_i,b_i\leq 100 n100,ai,bi100

solution

只有第一问,就是一个裸的二分图匹配

如果第二问不要求“在最少次数的前提下”,那么就是按费用建图跑最小割

所以这题的做法就是把它们结合起来

Δ = 1 0 6 \Delta=10^6 Δ=106 s s s i i i a i + Δ a_i+\Delta ai+Δ的边, i + n i+n i+n t t t b i + Δ b_i+\Delta bi+Δ的边,中间建inf

跑出来的最大流/ Δ \Delta Δ就是第一问,最大流% Δ \Delta Δ就是第二问

#include 
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

*/ 

你可能感兴趣的:(比赛总结)