2022/2/13

2022/2/13

2018南京I 网络流

最大流

建模方法如下:

  1. 怪兽向汇点t连一条边,流量为1,表示只能被杀一次
  2. 每个英雄向他能杀死的怪物集合连一条边,流量为1,表示可以杀。
  3. 源点像每个英雄连一条边,流量为1,表示这个英雄只能杀一个
  4. 源点向药水连一条边,容量为k,表示可以使用k次
  5. 药水向英雄连边,流量为1,表示每个英雄最多喝一次药水

参考代码

#include<bits/stdc++.h>
#define ll  long long
#define pii pair<long long , long long >
#define si size()
#define fi first
#define se second
#define pb push_back
using namespace std;
ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void Prin(ll x){if(x < 0){putchar('-');x = -x;}if(x > 9) Prin(x / 10);putchar(x % 10 + '0');}

const ll mod=1e9+7;
const ll inf=0x3f3f3f3f;
const int qs=1e6+7;

ll T,n,m,k,s,t,h;

ll head[qs],nxt[qs],to[qs],dis[qs],p;
void add(int fx,int tx,ll dx){
	to[p]=tx; dis[p]=dx; nxt[p]=head[fx]; head[fx]=p++;
    //反向边 
	to[p]=fx; dis[p]=0; nxt[p]=head[tx]; head[tx]=p++;
}

//Dinic
ll level[qs],cur[qs];
//level是各点到终点的深度,cur为当前弧优化的增广起点 
bool bfs(){//分层图 
	memset(level,-1,sizeof(level));
	level[s]=0;
	memcpy(cur,head,sizeof(head));
	cur[s]=head[s];
	queue<int> Q;
	Q.push(s);
	while(Q.si){
		int k=Q.front();
		Q.pop();
		for(int i=head[k];i!=-1;i=nxt[i]){
			if(dis[i]>0&&level[to[i]]==-1){
				level[to[i]]=level[k]+1;
				Q.push(to[i]);
				if(to[i]==t) return true;
			}
		}
	}
	return false;
}
ll dfs(int u,ll flow){
	if(u==t) return flow;
	
	ll ret=flow; //剩余的流量
	for(int i=cur[u];i!=-1&&ret>0;i=nxt[i]){
		cur[u]=i;// 当前弧优化
		//如果还能流下去 并且 更深 
		if(dis[i]>0&&level[to[i]]==level[u]+1){
			ll c=dfs(to[i],min(dis[i],ret));
			if(!c) level[to[i]]=-1; //剪枝,出去增广完毕的点 
			ret-=c; //剩余的水流被用了c
			dis[i]-=c;	//减权重 
			dis[i^1]+=c; //反向边加权重 
		}
	} 
	return flow-ret;//返回用掉的水流 
}
//END 


void build_map(){
	memset(head,-1,sizeof(head));
	s=0,h=n+m+1,t=h+1;
	add(s,h,k); //源点向药水建边 
	for(int i=1;i<=n;++i) add(h,i,1); //药水向人物建边
	for(int i=1;i<=n;++i) add(s,i,1); //源点向人建边 
	for(int i=1;i<=n;++i){
		int ki,x; ki=read();
		for(int j=1;j<=ki;++j){
			x=read();
			add(i,n+x,1);//人物向怪物建边 
		}
	}
	for(int i=1;i<=m;++i) add(n+i,t,1);//怪物向汇点建边 
}

void solve(){
	ll ans=0;
	while(bfs()){
		ans+=dfs(s,inf);
	}
	cout<<ans<<"\n";
}

int main(){
	n=read(),m=read(),k=read();
	build_map();
	solve(); 
	
	
}

诡异数字 (nowcoder.com)

数位dp

\(f[i][j][k]\)表示第i位,前面位的数字为 j ,数量为k时的状态。

参考代码

#include<bits/stdc++.h>
#define ll  long long
#define pii pair<long long , long long >
#define si size()
#define fi first
#define se second
#define pb push_back
using namespace std;
ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void Prin(ll x){if(x < 0){putchar('-');x = -x;}if(x > 9) Prin(x / 10);putchar(x % 10 + '0');}
 
const ll mod=20020219;
const ll inf=0x3f3f3f3f;
const int qs=1e6+7;
 
ll T,a[23],f[23][23][23],n,m,k,bit[23],len;
 
ll dfs(ll pos,ll pre,ll cnt,ll lim){
    if(cnt>a[pre]) return 0;
    if(!pos) return 1;
    if(!lim && f[pos][pre][cnt]!=-1) return f[pos][pre][cnt];
    ll up=lim ? bit[pos] : 9;
    ll ans=0;
    for(int i=0;i<=up;++i){
        //if(pre==i&&a[i]!=-1&&cnt==a[i]) continue;
        ans+=dfs(pos-1,i,(i==pre) ? cnt+1 : 1,lim&&i==up);
        ans%=mod;
    }
    if(!lim) f[pos][pre][cnt]=ans;
    return ans;
}
 
ll solve(int x){
    if(x<0) return 0;
    len=0;
    while(x){
        bit[++len]=x%10;
        x/=10;
    }
    return dfs(len,-1,0,1);
}
 
int main(){
    T=read();
    while(T--){
        memset(f,-1,sizeof(f));
        scanf("%lld%lld%lld",&n,&m,&k);
        for(int i=0;i<=11;++i) a[i]=inf;
        ll x,y;
        while(k--){
            scanf("%lld%lld",&x,&y);
            a[x]=min(a[x],y);
        }
        ll ans=solve(m)-solve(n-1);
        ans=(ans+mod)%mod;
        cout<<ans<<"\n";
    }
     
    return 0;
}
posted @ 2022-02-14 00:30  Suki_Sugar  阅读(24)  评论(0编辑  收藏  举报
Live2D