一、Average and Median
传送门:E - Average and Median (atcoder.jp)
题意:
在长度为n的一个序列中选择一些数,选取数的条件是:对于每一个 i ( 1 ≤ i < n ),其中a[i-1]和a[i]至少有一个要被选中。分别求可以取出来数的平均数、中位数最大值
思路:
1.最大平均数
将答案二分,若已知平均数答案ans,对于相邻两个数取谁就好决定了,满足选择的数与ans作差后的和的最大就行。求最大的过程,用动规。算出最大后,如果最大值比0大说明平均数答案还可以更大,在二分的右段继续。
dp[i][j]:
i=0:表示从第一个数到第j个数,不取aj,在此之前选择的数和ans作差后和的最大值。
i=1:取aj
转移方程:
dp[1][i]=max(dp[1][i-1]+a[i]-k,dp[0][i-1]+a[i]-k)
dp[0][i]=dp[1][i-1];//只有一个是因为a[i-1]和a[i]至少有一个要被选中。
2.最大中位数
同样也是二分ans,比ans大的数 如果占一半就是可以,大于一半说明中位数可以更大,小于一半说明应该更小。
动规最大的贡献;
temp=(a[i]>k)?1:-1;
dp[1][i]=max(f[1][i - 1]+temp,f[0][i - 1]+temp)
f[0][i]=f[1][i-1]
const int N = 3e5 + 10, mod = 998244353;
int n,a[N];
double dp[2][N];
bool average(double k)
{
dp[0][0] = dp[1][0] = 0.0;
for (int i = 1; i <= n; i++)
{
dp[0][i] = dp[1][i - 1];
dp[1][i] = max(dp[1][i - 1] + a[i] - k, dp[0][i - 1] + a[i] - k);
}
if (dp[0][n] >= 0 || dp[1][n] >= 0)
return true;
else
return false;
}
bool median(int k)
{
dp[0][0] = dp[1][0] = 0.0;
for (int i = 1; i <= n; i++)
{
int temp = (a[i] >= k) ? 1 : -1;
dp[0][i] = dp[1][i - 1];
dp[1][i] = max(dp[1][i - 1]+temp, dp[0][i - 1] + temp);
}
if (dp[0][n] >0 || dp[1][n] >0)
return true;
else
return false;
}
void solo()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
double left = 0, right = 1000000000.0;
while (right - left >= 1e-6)
{
double middle = (left + right) / 2;
if (average(middle))
left = middle;
else
right = middle;
}
cout << right << endl;
int lleft = 0, rright = 1000000000;
while (rright>=lleft)
{
int middle = (lleft + rright) / 2;
if (median(middle))
lleft = middle+1;
else
rright = middle-1;
}
cout << rright << endl;
}
int main()
{
LL t=1;
//cin >> t;
while (t--)
{
solo();
}
}
二、Toss a Coin to Your Graph
传送门:
题意:一张图n个点m条边, 每个点有权值, 求长度为k-1(结点数为k)的路径的最大权值的最小值。
思路:
二分答案mid。只遍历点权小于等于mid的点,记录每条边,对于每次的mid,建新的图跑拓扑排序来判断这个图是否存在链长大于等k的链,或者是存在环。
Note that the function of existence of the answer relatively to the minimum value of the maximum in the path is monotonous.
单调性:若所选的点权值越大,越容易符合 k - 1
条边(答案越大,小于等于这个答案的点权就越多,就越容易构造出 k - 1
条边)。最小化最大值,二分来查找最大点权值,并最小化它。
If we were able to construct the path with maximum, not greater than x, we are able to construct the path with maximum, not greater than x+1. This leads to the idea of binary search the answer.
Let binary search to fix some integer x. We have to check, if there is a path in the graph, that consists of k−1 edges and the maximum on this path is not greater than x. In the beginning let's leave in consideration only vertices which values are not greater than x. Now we need to check if the needed path exist in the resulting graph.
If there is a cycle in the graph, there is a path of every length in it, so there is a path of length k−1. Otherwise we have a directed acyclic graph. Let's find a longest path in it and compare its length with k−1. Let's sort the graph topologically and calculate dp[v]— the length of the longest path in the graph that begins in vertex v, it's a well-known classical problem.
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+5;
const int inf=0x3f3f3f3f;
typedef long long ll;
int n,m,k;
struct Edge
{
int from,to;
};
vector<int> G[maxn];
vector<Edge> edge;
bool now[maxn];
int a[maxn],inDegree[maxn],len[maxn];
void first()
{
for(int i=1;i<=n;i++)//初始化
{
now[i]=0;
len[i]=-inf;
inDegree[i]=0;
}
}
bool NewGraph(int x)
{
first();
for(int i=1;i<=n;i++)//点权小于mid的部分放进去
{
if(a[i]<=x)
now[i]=1;
}
for(int i=0;i<edge.size();i++)//入度
{
Edge e=edge[i];
int u=e.from,v=e.to;
if(now[u]&&now[v])
inDegree[v]++;
}
//拓扑
queue<int>q;
for(int i=1;i<=n;i++)
{
if(now[i]&&!inDegree[i])//入度为0
{
q.push(i);
len[i]=1;
}
}
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=0;i<G[u].size();i++)
{
int v=edge[G[u][i]].to;
if(!now[v])//大于x
continue;
len[v]=max(len[u]+1,len[v]);//维护最大的路径长度
if(len[v]>=k)//找到最长路径了
return 1;
inDegree[v]--;
if(inDegree[v]==0)
q.push(v);
}
}
for(int i=1;i<=n;i++)
{
if(now[i]&&inDegree[i]>0)//有环,也是可以的
return 1;
}
return 0;//路径长度不够 还没环
}
signed main()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
edge.push_back({u,v});
G[u].push_back((int)edge.size()-1);//结点编号
}
if(k==1)
{
if(m==0)
cout<<a[1]<<endl;
else
cout<<*max_element(a+1,a+1+n)<<endl;
return 0;
}
int ans=inf;
int left=0,right=1e9;
while(left<=right)
{
int middle=(left+right)/2;
if(NewGraph(middle))
{
right=middle-1;
ans=min(ans,middle);//最小
}
else
left=middle+1;
}
if(ans==inf)
cout<<-1;
else
cout<<ans;
}