Average and Median、Toss a Coin to Your Graph

一、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;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序和三三总有一个能跑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值