【EOJ Monthly 2019.02 - E】中位数(二分 ,中位数 ,−1/1变换,dp求解DAG最长路)
題干:
E. 中位數(shù)
單測試點(diǎn)時(shí)限: 10.0 秒
內(nèi)存限制: 256 MB
“你的地圖是一張白紙,所以即使想決定目的地,也不知道路在哪里。”
QQ 小方最近在自學(xué)圖論。他突然想出了一個(gè)有趣的問題:
一張由 n 個(gè)點(diǎn),m 條邊構(gòu)成的有向無環(huán)圖。每個(gè)點(diǎn)有點(diǎn)權(quán) Ai 。QQ 小方想知道所有起點(diǎn)為 1 ,終點(diǎn)為 n 的路徑中最大的中位數(shù)是多少。
一條路徑的中位數(shù)指的是:一條路徑有 n 個(gè)點(diǎn),將這 n 個(gè)點(diǎn)的權(quán)值從小到大排序后,排在位置 ?n2?+1 上的權(quán)值。
輸入
第 1 行輸入兩個(gè)正整數(shù) n,m (1≤n≤106,1≤m≤106 ),表示結(jié)點(diǎn)數(shù)量和邊的數(shù)量。
第 2 行輸入 n 個(gè)由空格隔開的整數(shù) Ai (0≤Ai≤109 ),表示點(diǎn)權(quán)。
接下來 m 行,每行輸入兩個(gè)整數(shù) x,y (1≤x,y≤n ),表示有一條 x 指向 y 的單向邊,保證給出的圖是聯(lián)通的,可能存在重邊。
輸出
輸出一行包含一個(gè)整數(shù),表示最大的中位數(shù)。如果不存在任何一條起點(diǎn)為 1 ,終點(diǎn)為 n 的路徑,則輸出 ?1 。
樣例
Input
5 5 1 2 3 4 5 1 2 2 3 3 5 2 4 4 5Output
4解題報(bào)告:
考慮二分答案,我們需要驗(yàn)證路徑最大的中位數(shù)是否 ≥mid 。
我們把所有的點(diǎn)權(quán)做 ?1/1 變換,即 ≥mid 的點(diǎn)權(quán)變?yōu)?1 ,否則變?yōu)??1 。
根據(jù)題面路徑中位數(shù)的定義,我們可以發(fā)現(xiàn),如果這條路徑的中位數(shù) ≥mid ,那么做了 ?1/1 變換以后這條路徑上的點(diǎn)權(quán)和 ≥0 。
而我們現(xiàn)在需要知道的問題是路徑最大的中位數(shù)是否 ≥mid ,也就是說,最大的路徑點(diǎn)權(quán)是否 ≥0 。
跑一遍最長路就好了。而對于 DAG ,最長路只要 dp 一下,復(fù)雜度是保證 O(m) 。
AC代碼:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define pb push_back #define pm make_pair using namespace std; const int MAX = 2e6 + 5; ll val[MAX],b[MAX],a[MAX]; int head[MAX]; ll dis[MAX]; int tot,flag; bool vis[MAX]; int n,m; struct Node {int to;int ne; } e[MAX]; void add(int u,int v) {e[++tot].ne = head[u];e[tot].to = v;head[u] = tot; } void dfs(int cur,int rt) {if(vis[cur]) return ;vis[cur] = 1;if(cur == n) {flag = 1;dis[n] = val[n];return ;}dis[cur] = -0x3f3f3f3f;for(int i = head[cur]; i!=-1; i=e[i].ne) {int v = e[i].to;if(v == rt) continue;dfs(v,cur);dis[cur] = max(dis[cur],dis[v]);}dis[cur] += val[cur]; } bool ok(ll x) {for(int i = 1; i<=n; i++) {if(a[i] >= x) val[i] = 1;else val[i] = -1;}memset(vis,0,sizeof vis);dfs(1,-1);return dis[1] >= 0; } int main() {cin>>n>>m;memset(head,-1,sizeof head);for(int i = 1; i<=n; i++) scanf("%lld",val + i),a[i] = b[i] = val[i];//注意本題中點(diǎn)權(quán)為正 for(int u,v,i = 1; i<=m; i++) {scanf("%d%d",&u,&v);add(u,v);}memset(vis,0,sizeof vis);dfs(1,-1);if(flag == 0) {puts("-1");return 0 ;}sort(b+1,b+n+1);int x = unique(b+1,b+n+1) - b - 1;ll l = 1,r = x,mid,ans;while(l<=r) {mid = (l+r)>>1;if(ok(b[mid])) {ans = mid;l = mid+1;}else r = mid-1;}printf("%lld\n",b[ans]);return 0 ;}為啥這樣就超時(shí)。。:(有沒有大佬來解釋一下這樣記憶化為啥不對啊,,歡迎留言區(qū)討論)
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define pb push_back #define pm make_pair using namespace std; const int MAX = 2e6 + 5; ll val[MAX],b[MAX],a[MAX]; int head[MAX]; ll dis[MAX]; int tot,flag; int n,m; struct Node {int to;int ne; } e[MAX]; void add(int u,int v) {e[++tot].ne = head[u];e[tot].to = v;head[u] = tot; } ll dfs(int cur,int rt) {if(dis[cur] != -0x3f3f3f3f) return dis[cur];if(cur == n) {flag = 1;dis[n] = val[n];return dis[n];}for(int i = head[cur]; i!=-1; i=e[i].ne) {int v = e[i].to;if(v == rt) continue;ll tmp = dfs(v,cur);dis[cur] = max(dis[cur],tmp);}dis[cur] += val[cur];return dis[cur]; } bool ok(ll x) {for(int i = 1; i<=n; i++) {if(a[i] >= x) val[i] = 1;else val[i] = -1;}for(int i = 1; i<=n; i++) dis[i] = -0x3f3f3f3f;dfs(1,-1);return dis[1] >= 0; } int main() {cin>>n>>m;memset(head,-1,sizeof head);for(int i = 1; i<=n; i++) scanf("%lld",val + i),a[i] = b[i] = val[i];//注意本題中點(diǎn)權(quán)為正 for(int u,v,i = 1; i<=m; i++) {scanf("%d%d",&u,&v);add(u,v);}for(int i = 1; i<=n; i++) dis[i] = -0x3f3f3f3f;dfs(1,-1);if(flag == 0) {puts("-1");return 0 ;}sort(b+1,b+n+1);int x = unique(b+1,b+n+1) - b - 1;ll l = 1,r = x,mid,ans;while(l<=r) {mid = (l+r)>>1;if(ok(b[mid])) {ans = mid;l = mid+1;}else r = mid-1;}printf("%lld\n",b[ans]);return 0 ;}補(bǔ)充:如果要求:排在位置上的數(shù)。
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define pb push_back #define pm make_pair using namespace std; const int MAX = 2e6 + 5; ll val[MAX],b[MAX],a[MAX]; int head[MAX]; ll dis[MAX]; int tot,flag; bool vis[MAX]; int n,m; struct Node {int to;int ne; } e[MAX]; void add(int u,int v) {e[++tot].ne = head[u];e[tot].to = v;head[u] = tot; } void dfs(int cur,int rt) {if(vis[cur]) return ;vis[cur] = 1;if(cur == n) {flag = 1;dis[n] = val[n];return ;}dis[cur] = -0x3f3f3f3f;for(int i = head[cur]; i!=-1; i=e[i].ne) {int v = e[i].to;if(v == rt) continue;dfs(v,cur);dis[cur] = max(dis[cur],dis[v]);}dis[cur] += val[cur]; } bool ok(ll x) {for(int i = 1; i<=n; i++) {if(a[i] > x) val[i] = 1;else val[i] = -1;}memset(vis,0,sizeof vis);dfs(1,-1);return dis[1] <= 0; } int main() {cin>>n>>m;memset(head,-1,sizeof head);for(int i = 1; i<=n; i++) scanf("%lld",val + i),a[i] = b[i] = val[i];//注意本題中點(diǎn)權(quán)為正 for(int u,v,i = 1; i<=m; i++) {scanf("%d%d",&u,&v);add(u,v);}memset(vis,0,sizeof vis);dfs(1,-1);if(flag == 0) {puts("-1");return 0 ;}sort(b+1,b+n+1);int x = unique(b+1,b+n+1) - b - 1;ll l = 1,r = x,mid,ans;while(l<=r) {mid = (l+r)>>1;if(ok(b[mid])) {ans = mid;r = mid-1;}else l = mid+1;}printf("%lld\n",b[ans]);return 0 ;}/* 4 3 1 2 3 4 1 2 2 3 3 4*/另一種求DAG最長路的方法:?
#pragma GCC optimize(3,"Ofast","inline") #include<bits/stdc++.h> using namespace std; typedef long long ll; const int MAX=1000006; int n,m,v[MAX],d[MAX],g[MAX],p[MAX],t[MAX],vis[MAX]; vector<int>G[MAX]; inline int check(int x) {for(int i=1; i<=n; ++i)p[i]=v[i]>=x?1:-1,t[i]=-1e9,g[i]=d[i];t[1]=p[1];queue<int>q;q.push(1);for(int u; !q.empty(); q.pop())for(auto v:G[u=q.front()]) {if(t[v]<t[u]+p[v]) t[v]=t[u]+p[v];if(!--g[v]) q.push(v);}return t[n]>=0; } int main() {cin>>n>>m;for(int i=1; i<=n; ++i)scanf("%d",v+i);if(n==1)return 0*printf("%d\n",v[1]);for(int i=1,x,y; i<=m; ++i) {scanf("%d%d",&x,&y);G[x].push_back(y),++d[y];}queue<int>q;q.push(1),vis[1]=1;for(int u; !q.empty(); q.pop())for(auto v:G[u=q.front()])if(!vis[v]) vis[v]=1,q.push(v);for(int i=1; i<=n; ++i)if(!vis[i])for(auto v:G[i]) d[v]--;int l=0,r=1e9,mid,ans=-1;while(l<=r) {mid=(l+r)>>1;if(check(mid))ans=mid,l=mid+1;elser=mid-1;}printf("%d\n",ans); }?
總結(jié)
以上是生活随笔為你收集整理的【EOJ Monthly 2019.02 - E】中位数(二分 ,中位数 ,−1/1变换,dp求解DAG最长路)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spyblast.exe - spybl
- 下一篇: 【牛客 - 331D】炫酷路途(二进制枚