傳送門:>here
<
題意:思路分析給定正整數序列$x_1,...,x_n$
(1)計算其最長不下降子串行的長度s。
(2)計算從給定的序列中最多可取出多少個長度為s的不下降子串行。
(3)如果允許在取出的序列中多次使用$x_1$和$x_n$,則從給定序列中最多可取出多少個長度為$s$的不下降子串行。
題意首先就很坑:注意第二問中的取出二字,意味著乙個數字最多只能存在於乙個lis中。所以才會有第三問的假設
第一問
第一問很簡單,直接暴力$o(n^2)$就好了
後面的兩問需要借助於網路流。
第二問
直接解釋原理不清晰也不易懂,先
詳細闡述如何建模過程:
首先原序列做lis,其中$f[i]$表示以$a[i]$為結尾的lis的最大長度。對序列的每乙個數進行拆點,也就是分為$$。我們要讓s到t的每一條路徑都是乙個lis,因此若$f[i]=1$,則$x_i$的點連線源點(容量為1,後同),$f[i]=maxs$則$y_i$連線匯點。其中對於每乙個數字,連線有向邊$(x_i, y_i)$。並且在做lis的過程中,若發現一對數字滿足$a[j]<=a[i] 且 f[j]+1=f[i]$,則連有向邊$(y_j, x_i)$。最後做最大流即可
我們發現這道題又和二分圖有一點類似。經過剛才這樣的連邊之後,任一s到t的路徑都是乙個lis,並且容量為1保證了每個點不會被用兩次(與二分圖原理相同),因此最大流即為方案數
至於與二分圖不同的地方我們發現,我們每個點本身連了邊,最後lis的邊還是反過來加的。為什麼要這麼做呢?這是這道題最大的難點。
首先分析這種方法的正確性:經過現在這樣的建圖,y部分稱為了邊的起點,而x成為了終點。設有一條邊是$(y_a, x_b)$,則其到達$x_b$以後會順著自己的邊到達$y_b$,而後如果還存在一條邊$(y_b, x_c)$,它就會繼續來到$y_c$,以此類推最終到達t。其中$(x_i, y_i)$起到了過渡作用,並且保證了每個點只走一次
然後來講這種方法的必要性:為什麼正著連邊不行呢?原因在於我們源點與匯點的選擇。想象乙個序列$5,4,3,2,1$,按照目前這種方法自然會跑出最大流為5(每個點直接沿著自己的邊走了)。然而如果正著連邊自己反著連,那麼將會跑出0——我們並沒有給它乙個跑出去的機會
第三問
第三問其實就是在第二問的基礎上稍作了一些改動。$x_1和x_n$可以用無限次意味著可以去掉之前給這兩個點只能用一次的束縛,因此只需要把源點到1,n到匯點的邊的容量改為inf即可。注意由於現在起點是右側點了,所以$(x_1,y_1)$和$(x_n,y_n)$的容量務必也要改成inf
code
細節題。dinic不要打錯了不然完蛋
另外,再dfs的時候由於我們最初給s設的可增廣量為inf,而容量也是inf,這就不太對了,因為源點還有可能連更多的邊,inf就不夠了。因此第三問的時候容量改為inf/10保險一點
/*by dennyqi
*/#include
#include
#include
#include
#define r read()
#define max(a,b) (((a)>(b)) ? (a) : (b))
#define min(a,b) (((a)<(b)) ? (a) : (b))
using
namespace
std;
typedef
long
long
ll;const
int maxn = 10010
;const
int maxm = 10010
;const
int inf = 1061109567
;inline
intread()
intk,n,m,s,t,x,p,maxs;
int first[maxm*2],nxt[maxm*2],to[maxm*2],cap[maxm*2],flow[maxm*2],num_edge=-1
;int
level[maxn],cur[maxn];
queue
q;inta[maxn],f[maxn];
inline
void add(int u, int v, int c, int
f)inline
bool
bfs()}}
return level[t] != 0;}
int dfs(int u, int
a) }
return
ans;
}inline
void
dinic()
printf(
"%d\n
", ans);
}int
main()
maxs = 0
;
for(int i = 1; i <= n; ++i)
}++f[i];
maxs =max(maxs, f[i]);
for(int j = 1; j < i; ++j)}}
printf(
"%d\n
", maxs);
for(int i = 1; i <= n; ++i)
if(f[i] ==maxs)
}dinic();
memset(flow,
0, sizeof
(flow));
memset(first, -1, sizeof
(first));
memset(nxt,
0, sizeof
(nxt));
memset(f,
0, sizeof
(f));
s = 1, t = 2*n+2; num_edge = -1
;
for(int i = 1; i <= n; ++i)
add(i
<<1, i<<1|1, 1, 0
); add(i
<<1|1, i<<1, 0, 0
); }
maxs = 0
;
for(int i = 1; i <= n; ++i)
}++f[i];
maxs =max(maxs, f[i]);
for(int j = 1; j < i; ++j)}}
for(int i = 1; i <= n; ++i)
else
}if(f[i] ==maxs)
else
; }
}dinic();
return0;
}
網路流24題之最長不下降子串行
對於第一問直接n 2dp計算 第二問建圖跑網路流 第三問將起始與結尾流量開大 建邊的時候要嚴格按照子串行求法建 by 大奕哥 1 include2 using namespace std 3const int n 10005 4 inthead n d n f n a n 5int n,m,cnt ...
網路流24題 最長不下降子串行問題
luogu 2766 最長不下降子串行問題 傳送門第一問 o n 2 的dp求lis 為了下面敘述方便,我們將dp過程講一遍 子狀態 dp i 表示以a i 結尾的lis長度 初始條件 dp i 1 狀態轉移方程 dp i dp j 1 j 第二問 我們發現若a j 加上a i 可以構成乙個不下降子...
網路流24題 最長不下降子串行問題(最大流)
題目鏈結 這個題目有三個要求出來的 計算其最長不下降子串行的長度s。計算從給定的序列中最多可取出多少個長度為s的不下降子串行 如果允許在取出的序列中多次使用x1和xn,則從給定序列中最多可取出多少個長度為s的不下降子串行 自己的想法 對於 問題1 直接用那個動態規劃去跑下,就求出答案len了,這是會...