首页 > 其他分享 >P9755 [CSP-S 2023] 种树 题解

P9755 [CSP-S 2023] 种树 题解

时间:2024-02-28 15:13:23浏览次数:38  
标签:__ ch return int 题解 long P9755 inline CSP

首先考虑如何求出第 \(i\) 棵树在 \([l,r]\) 时间段能长多高。这个东西可以差分一下然后等差数列求和。放一下代码:

inline lll calc(int i,int x){
	if(c[i]>=0){
		return (lll)b[i]*x+(lll)c[i]*x*(x+1)/2;
	}else{
		int d=(b[i]-1)/(-c[i]);
		if(x<=d) return (lll)b[i]*x+(lll)c[i]*x*(x+1)/2;
		else return (lll)b[i]*d+(lll)c[i]*d*(d+1)/2+(x-d);
	}
}
inline lll calc(int i,int l,int r){
	return calc(i,r)-calc(i,l-1);
}

注:要开 __int128。

考虑二分完成时间 \(t\),则转化为判定能否在 \(t\) 时间之前完成任务。考虑对于每个点处理出 \(lim_i\) 表示最晚的合法栽种时间,再套一层二分解决。

接下来考虑贪心,按 \(lim\) 从小到大处理所有点。记录当前时间,若 \(1\sim x\) 的路径上还有其他点没有栽种(很明显是一段后缀),就先在这些点上种树,并更新时间。最后判断每个点的时间是否都满足条件即可。

时间复杂度 \(O(n\log^2 V)\),可以通过。实现起来可能稍微有点卡常,将不必要的 __int128、long long 换成 int 即可(只有 \(a\) 需要 long long)。

代码:

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define gt getchar
#define pt putchar
#define fst first
#define scd second
#define SZ(s) ((int)s.size())
typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;
typedef unsigned int uint;
const int N=1e5+5;
using namespace std;
using namespace __gnu_pbds;
typedef pair<int,int> pii;
inline bool __(char ch){return ch>=48&&ch<=57;}
template<class T> inline void read(T &x){
	x=0;bool sgn=0;char ch=gt();
	while(!__(ch)&&ch!=EOF) sgn|=(ch=='-'),ch=gt();
	while(__(ch)) x=(x<<1)+(x<<3)+(ch&15),ch=gt();
	if(sgn) x=-x;
}
template<class T,class ...T1> inline void read(T &x,T1 &...x1){
	read(x);
	read(x1...);
}
template<class T> inline void print(T x){
	static char stk[70];short top=0;
	if(x<0) pt('-');
 	do{stk[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
    while(top) pt(stk[top--]);
}
template<class T> inline void printsp(T x){
	print(x);
	putchar(' ');
}
template<class T> inline void println(T x){
	print(x);
	putchar('\n');
}
int n;
struct Edge{
	int to,nxt;
}e[N<<1];
int head[N],cnt;
inline void add_edge(int f,int t){
	e[++cnt].to=t;
	e[cnt].nxt=head[f];
	head[f]=cnt;
}
inline void add_double(int f,int t){
	add_edge(f,t);
	add_edge(t,f);
}
ll a[N];
int b[N],c[N];
inline lll calc(int i,int x){
	if(c[i]>=0){
		return (lll)b[i]*x+(lll)c[i]*x*(x+1)/2;
	}else{
		int d=(b[i]-1)/(-c[i]);
		if(x<=d) return (lll)b[i]*x+(lll)c[i]*x*(x+1)/2;
		else return (lll)b[i]*d+(lll)c[i]*d*(d+1)/2+(x-d);
	}
}
inline lll calc(int i,int l,int r){
	return calc(i,r)-calc(i,l-1);
}
int fa[N],p[N],lim[N];
bool vis[N];
void dfs(int u,int fa){
	::fa[u]=fa;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v!=fa) dfs(v,u);
	}
}
inline bool check(int ti){
	for(int i=1;i<=n;++i){
		if(calc(i,ti)<a[i]) return 0;
		int l=0,r=1e9;
		auto chk=[&](int x){
			return calc(i,x,ti)>=a[i];
		};
		while(l<r){
			int mid=(l+r+1)>>1;
			if(chk(mid)) l=mid;
			else r=mid-1;
		}
		lim[i]=l,vis[i]=0,p[i]=i;
	}
	sort(p+1,p+n+1,[](int x,int y){
		return lim[x]<lim[y];
	});
	int now=0;
	for(int i=1;i<=n;++i){
		int u=p[i];
		if(vis[u]) continue;
		int v=u;
		while(!vis[v]&&v){
			vis[v]=1;
			now++;
			v=fa[v];
		}
    	if(now>lim[u]) return 0;
	}
	return 1;
}
signed main(){
	read(n);
	for(int i=1;i<=n;++i) read(a[i],b[i],c[i]);
	for(int u,v,i=1;i<n;++i){
		read(u,v);
		add_double(u,v);
	}
	dfs(1,0);
	int l=0,r=1e9;
	while(l<r){
		int mid=l+((r-l)>>1);
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	println(l);
	return 0;
}

标签:__,ch,return,int,题解,long,P9755,inline,CSP
From: https://www.cnblogs.com/syzqwq/p/18040454

相关文章

  • A-E题解
    A:对于任意一个满足条件的2*2矩阵,要么3个R和1个W,要么3个W和1个R。我们以3个R和1个W举例,只有以下4中情况满足:RR   RR   RW   WRRW  WR  RR   RR所以一种构造方法如下:奇数行全部放R;偶数行奇数列放R,偶数列放W即可。voidsolve(){intn;......
  • CF756D Bacterial Melee 题解
    给一个\(O(n^2)\)的做法。考虑从左到右扫描最终得到的字符串,如果当前的字符和前一个字符相同,就删掉这个字符。由题意可知,最终得到的字符串一定是\(s\)的一个子序列。我们定义状态\(dp[i][j]\)表示:当前子序列的最后一个字符是\(s[i]\),已经填完了最终字符串的前\(j\)个字......
  • CF1034E Little C Loves 3 III 题解
    这道题与P6097【模板】子集卷积基本相同,但是每个元素的值属于\([0,3]\),且\(n\le21\),时限\(\rm1s\)。在做P6097这道题的时候,我们多开了一维用来记录二进制下\(1\)的个数。但是这道题每个元素的值只属于\([0,3]\),我们可以用一种十分巧妙的方法:我们设\(f(x)\)表示\(......
  • P2065 [TJOI2011] 卡片 题解
    看大家建图时中间都连了质数点,发一个不用质数点的解法。我们可以先从源点向每一个蓝色卡片对应的点连一条边,再从每一个红色卡片对应的点向汇点连一条边。如果两张卡片可以一起拿走,那就在它们之间连一条边(蓝色连到红色),这些边的最大流量都是\(1\)。建好图以后我们就可以直接用Di......
  • CF111D Petya and Coloring 题解
    很明显这是一道组合题。首先特判一下,当\(m=1\)时,答案就是\(k^n\)。对于\(m>1\)的情况,我们可以得出一个结论:对于沿格子的线穿过的任何垂直线,会将棋盘分成两个非空的部分,这两个部分中的不同颜色的数量相同且总是不变。设这个不同颜色的数量为\(i\),那么左边这部分的颜色一定......
  • [USACO13MAR]Farm Painting S 题解
    看大家好多写的都用了四维偏序,给一个不用偏序的解法。简化一下题目,就是给你\(n\)个矩形,第\(i\)个矩形用\((x1_i,y1_i,x2_i,y2_i)\)表示,问你有多少个\(i\)满足:不存在另一个\(j\)使得\(x1_j\lex1_i\lex2_j\wedgey1_j\ley1_i\ley2_j\)。我们从左到右扫描每一个......
  • CF799D题解
    CF799D这里更容易进入且有翻译题意给定一个长宽为\(a\)和\(b\)的目标矩形、一个宽高为\(h\)和\(w\)的初始矩形及\(n\)个操作\(a_i\)。对于每个操作,可以将初始矩形的宽或高乘以\(a_i\),求使目标矩形能放入初始矩形的最少操作(目标矩形可以旋转90度)。解析这题算是......
  • [ABC303Ex] Constrained Tree Degree 题解
    AtCoder题面洛谷题面如果每个点的度数都知道了,那问题就转化成了P2290[HNOI2004]树的计数,直接求Prufer序列的个数即可,因为一个度数为\(d_i\)的点在Prufer序列中的出现次数是\(d_i-1\),所以答案是:\(\frac{(n-2)!}{\prod_{i=1}^{n}(d_i-1)!}\)。可以把\((n-2)!\)放到......
  • [COI2009] PLAHTE 题解
    首先对于每一个矩形,若\(x_2<0\),就将\(x_1,x_2\)均乘上\(-1\)再交换,对于\(y_1,y_2\)也做同样的操作。我们建立一个操作序列a[0~1000],和一个数组\(d\),每一个操作用\((x,y)\)表示,就是在\(d\)内把所有\(0\)到\(x\)的位置加上\(y\)。我们再定义一种新的操作\([x,y......
  • [USACO11NOV]Binary Sudoku G 题解
    定义一个\(3\times3\)的表格\(a\),表示每个小九宫格内1的个数的奇偶状态。再定义两个长为\(9\)的数组\(c0,c1\),表示每行每列上1的个数的奇偶状态。当\(d_{i,j}\)取反时,会将\(a_{[\frac{i}{3}],[\frac{j}{3}]},c0_i,c1_j\)各取反一次。要将\(a_{i,j}\)全部变为0......