首页 > 其他分享 >CF1951I Growing Trees

CF1951I Growing Trees

时间:2024-04-30 14:33:47浏览次数:20  
标签:head int ll mid Trees rest cnt Growing CF1951I

My Blogs

CF1951I Growing Trees

首先考虑确定了 \(x_i\) 如何判定是否合法。可以很容易的找出这样一个必要条件:\(\forall i,x_i\leq k\)。

这是两个点的情况,考虑点数更多的情况。手玩之后可以推广到:对于任意导出子图,要求其内部的边数 \(\leq k(|S|-1)\)。这个条件也是充分的,证明类似 \(\text{Hall}\) 定理。

可以先把二次函数差分,这样可以算出每新加一条边的代价,是一个一次函数。

每次贪心的选择当前边权最小的边把其 \(x_i\) 加一,正确性应该可以用拟阵去证但是不会

然后就需要判定是否仍然合法,\(G(S)\leq k(|S|-1)\) 等价于 \(G(S)-k|S|\leq -k\),是最大权闭合子图,选定一条边就需要选定其两端点,点的收益是 \(-k\),边的收益是 \(x_i\)。

但是由于代价一定是负的,直接跑最大流肯定是把和源点连着的边全都切了,一定会跑出来 \(0\)。

所以必须要枚举一个点(原图中的)强制选进来然后跑最大流。这部分其实可能也可以用退流的方式,即每次退掉一个原图中的点对应的点连向 \(T\) 的流量,但是这个题 \(nm\) 数据范围都很小,暴力就能过。

现在问题在于 \(k\) 非常大。考虑求出每条边最多加多少次,在全局做值域从小到大的做这个过程,每次需要找到接下来会爆的是哪条边。可以直接二分或倍增,即确定一个值,然后把当前所有没有爆的边权值小于等于当前值的边都加进来,即 \(a_ix^2+b_ix-(a_i(x-1)^2+b_i(x-1))\leq mid\),然后做上述的判定。用这种方式找到最大的 \(mid\) 满足其是合法中的最大值。

接下来会出现问题:可能有多条边的下一个权值是 \(mid+1\),这样不能确定到底是哪条边爆了。

一个处理技巧是:先让所有边的权值都小于等于 \(mid\)。然后扫所有没有爆的边,把这条边的上界改成 \(mid+1\) 再跑一遍判定,如果不合法,那这条边一定爆了,这条边的上界再回到 \(mid\),同时求出了这条边的 \(x_i\)。否则这条边的上界就是 \(mid+1\)。注意上述过程每条边的 \(mid\) 是不同的。

这样至少会有一条边爆掉。总复杂度是 \(\mathcal O(m(\log V+m)\text{Flow}(m,m))\),可以通过。

int T_,n,m,k,x[110];
ll L,R,mid,v[3010],ans;
int S,T,cnt,head[110],now[110],d[110],to[3010],nex[3010];
inline void Add(int x,int y,ll z){to[++cnt]=y,nex[cnt]=head[x],head[x]=cnt,v[cnt]=z;}
inline void add(int x,int y,ll z){Add(x,y,z),Add(y,x,0);}
queue<int> q;
inline bool bfs()
{
	while(!q.empty())q.pop();
	memset(d,0,sizeof(d)),q.e(S),d[S]=1,now[S]=head[S];
	while(!q.empty())
	{
		int nw=q.front();q.pop();
		for(int i=head[nw];i;i=nex[i])
		{
			if(!d[to[i]]&&v[i])
			{
				d[to[i]]=d[nw]+1,now[to[i]]=head[to[i]],q.e(to[i]);
				if(to[i]==T)return 1;
			}
		}
	}
	return 0;
}
ll dinic(int x,ll flow)
{
	if(x==T)return flow;
	ll rest=flow,t;
	for(int i=head[x];i&&rest;i=nex[i])
	{
		if(!v[i]||d[to[i]]!=d[x]+1)continue;
		t=dinic(to[i],min(v[i],rest)),rest-=t,v[i]-=t,v[i^1]+=t;
		if(!t)d[to[i]]=0;
	}
	return flow-rest;
}
struct Node{int x,y,z,t;}b[110];
bitset<110> vis,can;
inline bool chk(ll mid)
{
	ll maxn=-INF,v;
	for(int j=1;j<=n;++j)
	{
		memset(head,0,sizeof(head)),cnt=1,ans=-k;
		for(int i=1;i<=n;++i)if(i==j);else add(i,T,k);
		for(int i=1;i<=m;++i)
		{
			if(vis[i])add(S,i+n,x[i]),ans+=x[i];
			else
			{
				v=max(0ll,(mid+can[i]+b[i].z-b[i].t)/2/b[i].z);
				if(v>k)ans+=inf;
				add(S,i+n,v),ans+=v;
			}
			add(i+n,b[i].x,INF),add(i+n,b[i].y,INF);
		}
		while(bfs())while((v=dinic(S,INF)))ans-=v;
		Mmax(maxn,ans);
	}
	return maxn<=-k;
}
inline void mian()
{
	read(T_);
	while(T_--)
	{
		read(n,m,k),S=n+m+1,T=S+1,vis.reset(),L=0;
		for(int i=1;i<=m;++i)read(b[i].x,b[i].y,b[i].z,b[i].t);
		for(int j=1;j<=m;++j)
		{
			R=1e18;
			while(L<R)
			{
				mid=L+((R-L+1)>>1);
				if(!chk(mid))R=mid-1;
				else L=mid;
			}
//				cerr<<"CLEAR:\n";
//				cout<<chk(45)<<endl;
//				exit(0);
//				cout<<chk(19,1);exit(0);
//				cout<<L<<" "<<chk(L)<<" "<<chk(L+1)<<endl;
			can.reset();
			for(int i=1;i<=m;++i)
			{
				if(vis[i])continue;
				can[i]=1;
				if(!chk(L))can[i]=0;
			}
			for(int i=1;i<=m;++i)
			{
				if(vis[i])continue;
				if(!can[i])
				{
					x[i]=max(0ll,(L+b[i].z-b[i].t)/2/b[i].z);
					vis[i]=1;break;
				}
			}
			can.reset();
		}
		ans=0;
		for(int i=1;i<=m;++i)ans+=1ll*x[i]*x[i]*b[i].z+1ll*x[i]*b[i].t;
		write(ans,'\n');
	}
}

标签:head,int,ll,mid,Trees,rest,cnt,Growing,CF1951I
From: https://www.cnblogs.com/WrongAnswer90/p/18167978

相关文章

  • JDK源码分析-TreeSet
    概述TreeSet是Java集合框架中用于存储唯一元素的树形数据结构,它实现了NavigableSet接口,这意味着TreeSet中的元素不仅是有序的,还支持一系列的导航方法。TreeSet的内部实现主要依赖于TreeMap,通过TreeMap的键来维护元素的排序。 类图从以上类图可以看到,TreeSet实现了三个接口,......
  • AGC014D Black and White Trees
    传送门[AGC014D]BlackandWhiteTree给出一颗\(N\)个节点组成的树,每个节点都可以被染成白色或者黑色;有高桥(先手)和青木(后手)两个人————高桥可以把任意一个点染成白色,青木则可以把任意一个点染成黑色,每个点只可染色一次。重复上述操作直到所有点都被染色后,只执行一次执行......
  • 手把手教你做阅读理解提高001-Camping:Finding Myself and Growing Strong-露营:在成长
    PDF格式公众号回复关键字:ZKYDT001阅读理解技巧,在帮助读者有效获取和理解文本信息方面发挥着重要作用,熟练掌握如下6个技巧,可快速突破阅读理解1预览文章结构在开始深入阅读之前,快速浏览文章的标题、段落开头和结尾,可以迅速把握文章的主题、大致内容和结构标题通常能概括文......
  • py_trees Sequence节点参数: memory=True | False
    Python行为树py_trees的一种注意情况:memory=True|Falsepy_trees…composites.Sequence(name=“root”,memory=True)官方文档是这样写的Ifconfiguredwithmemoryandachildreturnedwithrunningontheprevioustick,itwillproceeddirectlytotherunn......
  • TreeSet自定义对象compareTo(Object o)方法
    java小白,最近学到TreeSet,我们都知道在存储自定义对象时,需要使用Comparable或使用Comparator存储。刚刚碰到这样一段代码。publicclassPersonimplementsComparable{intage;Stringname;Person(intage,Stringname){this.age=age;th......
  • 【容器源码篇】Set容器(HashSet,LinkedHashSet,TreeSet的特点)
    文章目录⭐容器继承关系......
  • A Growing Tree
    以前在NK竞赛队的时候,做过一道考试题目,并查集离线,倒着处理这道题目是一样的,我们发现一个点只有在添加之后才会被更新,也就是只与这个点被添加的操作之后的操作有关,所以我们考虑倒着处理接下来就变成了一个模板问题:加一个子树,查询单点这个问题我们用dfs序解决(看到子树问题,想dfs序......
  • 说说Vue 3.0中Treeshaking特性?举例说明一下?
    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一、是什么Treeshaking 是一种通过清除多余代码方式来优化项目打包体积的技术,专业术语叫 Deadcodeelimination简单来讲,就是在保持代码运行结果不变的前提下,去除无用的代码如果把代码打包比作制作蛋糕,传统......
  • Codeforces 264E Roadside Trees
    首先考虑时间增长的问题,设第\(i\)棵树的种植时间为\(t_i\)。那么第\(x\)棵树比第\(y\)棵树高就是\(h_x+(t_y-t_x)>h_y\),也就是\(h_x-t_x>h_y-t_y\)。所以可以直接用\(h_i-t_i\)当作第\(i\)棵树的高度,即\(h'_i\leftarrowh_i-t_i\)。对于增加,考虑......
  • P4666 [BalticOI 2011 Day1] Growing Trees题解(平衡树思想)
    自己第一道不看题解写出来的紫题,庆祝一下(没初始化种子导致调了30min)这是一个fhq-treap的题解思路来源:首先看题目,因为是序列上的问题,不难想到是一道数据结构题。首先看到操作C:对于这种操作,我们可以用平衡树解决,具体方法是,将树split成\(<min,min\lex\lemax,>max\)这......