1. 程式人生 > 其它 >1015 德才論(超時)

1015 德才論(超時)

前言

zkw真滴快!

題目

洛谷

洛谷(加強版)

講解

剛開始思考這道題的時候,我試圖解決乾淨餐巾->髒餐巾 和 髒餐巾->乾淨餐巾的轉移,發現不會,於是自閉。

顯然我們不能把乾淨餐巾和髒餐巾混為一談,所以我們拆點,可以視作將一天拆為早上與晚上,早上收到乾淨餐巾並使用餐巾,晚上獲得髒餐巾,洗餐巾可以轉移到某一天的早上。

那麼我們怎麼強制早上使用餐巾並轉移到晚上呢?

拆一下,我們可以看成是早上一定要使用乾淨餐巾,晚上一定要獲得髒餐巾。

所以我們可以直接從早上向匯點連邊,表示使用餐巾,源點向晚上連邊表示獲得餐巾。

剩下的連邊就很好解決了。

  1. 早上向匯點連邊,表示使用餐巾。
  2. 源點向晚上連邊,表示獲得餐巾。
  3. 匯點向早上連邊,表示購買餐巾。
  4. 頭天晚上向下一天晚上連邊,表示懶狗不想洗餐巾。
  5. 晚上向某個白天連邊,表示洗餐巾。

如果用多路增廣的 zkw費用流,可以直接把 \(N\le 2\times 10^5\) 的加強版衝過去,加強版的正解應該是我之前思考的討論+二分/三分,這裡不做拓展。

程式碼

加強版
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std; 

typedef long long LL;
const int MAXN = 400005;
const int INF = 0x3f3f3f3f;
int days,p,qd,qc,sd,sc;//quick days; quick cost; slow..

LL Read()
{
	LL x = 0,f = 1;char c = getchar();
	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int head[MAXN],tot = 1,cur[MAXN];
struct edge{
	int v,w,c,nxt;
}e[MAXN<<3];
void Add_Edge(int u,int v,int w,int c){
	e[++tot] = edge{v,w,c,head[u]};
	head[u] = tot;
}
void Add_Double_Edge(int u,int v,int w,int c){
	Add_Edge(u,v,w,c);
	Add_Edge(v,u,0,-c);
}

int dis[MAXN],maxflow;
LL mincost;
bool inq[MAXN],vis[MAXN];
bool bfs(int S,int T){
	for(int i = 1;i <= T;++ i) dis[i] = INF;
	queue<int> q; q.push(S); dis[S] = 0;
	while(!q.empty()){
		int x = q.front(); q.pop(); inq[x] = 0;
		for(int i = head[x],v; i ;i = e[i].nxt)
			if(e[i].w && dis[x] + e[i].c < dis[v = e[i].v]){
				dis[v] = dis[x] + e[i].c;
				if(!inq[v]) inq[v] = 1,q.push(v);
			}
	}
	return dis[T] < INF;
}
int dfs(int x,int flow,int T){
	if(x == T) return flow;
	int ret = 0; vis[x] = 1;
	for(int &i = cur[x],v; i ;i = e[i].nxt)
		if(e[i].w && dis[x] + e[i].c == dis[v = e[i].v] && !vis[v]){
			int dz = dfs(v,Min(flow-ret,e[i].w),T);
			e[i].w -= dz; e[i^1].w += dz;
			if((ret += dz) == flow) break;
		}
	vis[x] = 0;
	return ret;
}
void MFMC(int S,int T){
	while(bfs(S,T)){
		for(int i = 1;i <= T;++ i) cur[i] = head[i];
		int ret = dfs(S,INF,T);
		maxflow += ret; mincost += 1ll * ret * dis[T];
	}
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	days = Read(); qd = Read(); sd = Read(); qc = Read(); sc = Read(); p = Read();
	int S = days<<1|1,T = S+1,sx = days<<1;
	for(int i = 1;i <= days;++ i){
		int nd = Read(),day = (i<<1)-1,night = day+1;
		Add_Double_Edge(day,T,nd,0);//use napkins
		Add_Double_Edge(S,night,nd,0);//gain dirty napkins
		Add_Double_Edge(S,day,INF,p);//purchase
		if(night+2 <= sx) Add_Double_Edge(night,night+2,INF,0);//lazy dog
		if(day+(qd<<1) <= sx) Add_Double_Edge(night,day+(qd<<1),INF,qc);//quick wash
		if(day+(sd<<1) <= sx) Add_Double_Edge(night,day+(sd<<1),INF,sc);//slow wash
	}
	MFMC(S,T);
	Put(mincost,'\n');
	return 0;
}