2020.11.30 比赛总结&题解合集

倒数第三场 OI 比赛。
前面做 T1,T2 的时候还觉得出题人挺良心的…直到看到了 T3,T4…
经过了这么多次跌宕起伏之后,成绩也真没想象中那么重要了。如果 NOIP 出现了挂题…也就只能认命吧…

Part 1 总结

期望得分: 100 + 80 + 30 + 20 = 230 100+80+30+20=230 100+80+30+20=230
实际得分: 100 + 100 + 25 + 15 = 240 100+100+25+15=240 100+100+25+15=240
今天真没有什么失误,但就是因为太菜被吊打了。
T2 想不到评测机还挺快的,本地极限数据跑了 10s…
本来就没有想过 NOIP 会出斜率优化板块的知识,结果今天一来就上了一个李超线段树,当场去世。
今天特别坑的一点是 T3/T4 两个人口普查的点都需要特判,结果写了暴力根本没管人口普查的点,直接去世了…
出题人也太假了吧…整个题面到处都在强调这套题非常的水…

Part 2 题解

T1- ZZH 的游戏(game)

为了方便思考,我们考虑先二分分数的最小值 l i m lim lim,再 check 它是否合法。
一个很显然的模拟方法就是每次轮流选一个人走到在另外一个人不动时满足限制能走到的编号最小的点。
容易发现这样做我们两个人的编号都是递减的,直到两个人都递减到 1 1 1。如果一轮中两个人都只能够停在原地,那么这个限制就不合法。
用并查集模拟即可。(其实可以预处理,但是我懒
复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)

T2- ZZH 与背包(knapsack)

算法1

一个显然的做法就是 meet-in-middle。
首先将询问拆分成两个查询容量 ≤ x \leq x x 的方案数的询问。
考虑将其分成两个集合之后,两边分别有 2 a , 2 b 2^a,2^b 2a,2b 个物品 a i , b j a_i,b_j ai,bj,我们对这两堆物品分别排序,然后用双指针找到对于第一堆的物品 i i i 使得 a i + b j ≤ x a_i+b_j\leq x ai+bjx 最大的 j j j
这样做的复杂度是 O ( q 2 n 2 ) O(q2^{\frac{n}{2}}) O(q22n) 的,由于常数还算小,可以通过。

算法2

题解给出了一种很妙的做法:
仍然考虑 meet-in-middle,但是这一次我们将前一半与询问结合在一起。具体来说,我们可以选择一些物品将它的权重 w i w_i wi 将一个询问的 x x x 变为 x − w i x-w_i xwi ,那么这样 2 q 2q 2q 个询问就被前一半分为了 2 q 2 a 2q2^a 2q2a 个询问。然后我们再用双指针通过对排好序的这 2 q 2 a 2q2^a 2q2a 个询问和后半部分的 2 b 2^b 2b 的处理做到 O ( q 2 a + 2 b ) O(q2^a+2^b) O(q2a+2b)
首先这个排序的效率比较低,题解巧妙的采用了增量法。即假设我们已经对前 2 i 2^i 2i 个数排好了顺序,设其为 A i A_i Ai,现在考虑增加第 i + 1 i+1 i+1 个数,容易发现 { A i } \{A_i\} {Ai} { A i + v i + 1 } \{A_i+v_{i+1}\} {Ai+vi+1} 都是有序的,我们采用和归并排序即可。
这样做的复杂度是 O ( 2 k + 2 k − 1 + …   ) = O ( 2 k + 1 ) O(2^{k}+2^{k-1}+\dots)=O(2^{k+1}) O(2k+2k1+)=O(2k+1)。(实际上这东西常数极大跟直接排没什么区别)。
考虑当 2 q 2 a = 2 b 2q2^a=2^b 2q2a=2b 时取到最优(且 a + b = n a+b=n a+b=n),由于数据范围的限制,容易发现在 a = 15 , b = 25 a=15,b=25 a=15,b=25 时取到最优,此时的复杂度就是 O ( q 2 n ) O(\sqrt{q2^n}) O(q2n )
但是现实是…这个做法空间开销过大,虽然在不开 O2 时比 O ( q 2 n 2 ) O(q2^{\frac{n}{2}}) O(q22n) 稍微快一点,但开了 O2 就被爆踩了。

T3- ZZH 与计数(counting)

复杂度非常玄妙的题目。
考虑一个比较显然的性质:对于在二进制下包含相同 1 的个数的两个数 x , y x,y x,y ,它们变为其他数的概率分布显然是类似的。也就是,我们的变化的概率与这个数的 0 / 1 0/1 0/1 具体在那些位置并不相关。
首先我们枚举二进制数 1 1 1 的个数有 x x x 个,在这个情况下求出这些数的概率分布。
考虑定义 f t , i , j f_{t,i,j} ft,i,j 表示当前已经操作了 t t t 次,现在这个数已经有 i i i 个从 1 1 1 变成了 0 0 0,有 j j j 个从 0 0 0 变成了 1 1 1 的概率。
转移比较简单,都是 p 2 x \frac{p}{2^x} 2xp 乘上一个组合数的形式。
由于 m m m 非常大,我们用矩阵乘法来优化它,复杂度是 O ( n 7 log ⁡ m ) O(n^7\log m) O(n7logm)
相信看到这里你一定满是问号,因为粗略算一下就可以发现这大概是 1.2 × 1 0 10 1.2\times 10^{10} 1.2×1010 数量级的。
但实际上这个算法的准确复杂度是 O ( log ⁡ m ∑ i = 0 n ( i × ( n − i ) ) 3 ) O(\log m\sum^n_{i=0} (i\times (n-i))^3) O(logmi=0n(i×(ni))3) ,写个程序可以算出大概只有 8 × 1 0 7 8\times 10^7 8×107,也就相当于一个大概 1 128 \frac{1}{128} 1281 的常数。
现在考虑这些概率分布对实际的贡献和。
定义 g i , S , j , k g_{i,S,j,k} gi,S,j,k 表示现在已经处理了最低的 i i i 位,还需要有 j j j 个数从 1 1 1 变为 0 0 0,有 k k k 个数从 0 0 0 变为 1 1 1 的贡献之和。
初始状态是 g 0 , S , j , k g_{0,S,j,k} g0,S,j,k ,对应的是当 x = b i t c o u n t ( S ) x=bitcount(S) x=bitcount(S) f n , j , k ( x j ) ( n − x k ) \frac{f_{n,j,k}}{\binom{x}{j}\binom{n-x}{k}} (jx)(knx)fn,j,k 的和。
当前二进制位不为 1 1 1 时:
g i + 1 , S , j , k = g i , S , j , k + g i , S + 2 i , j + 1 , k g_{i+1,S,j,k}=g_{i,S,j,k}+g_{i,S+2^i,j+1,k} gi+1,S,j,k=gi,S,j,k+gi,S+2i,j+1,k
当前二进制位为 1 1 1 时:
g i + 1 , S , j , k = g i , S , j , k + g i , S − 2 i , j , k + 1 g_{i+1,S,j,k}=g_{i,S,j,k}+g_{i,S-2^i,j,k+1} gi+1,S,j,k=gi,S,j,k+gi,S2i,j,k+1
最终状态显然是 g n , 0 , 0 , 0 , … , g n , 2 n − 1 , 0 , 0 g_{n,0,0,0},\dots,g_{n,2^n-1,0,0} gn,0,0,0,,gn,2n1,0,0
这样做复杂度是 O ( n 3 2 n ) O(n^32^n) O(n32n) ,粗算一下也不太行。
但是注意到有 i + j + k ≤ n i+j+k\leq n i+j+kn,这意味这良好的枚举方式能让这个 dp 做到 O ( ( n 3 ) 2 n ) O(\binom{n}{3}2^n) O((3n)2n),而这个的数量级也只有 8 × 1 0 7 8\times 10^7 8×107 左右,能够接受。
复杂度: O ( n 7 128 log ⁡ m + ( n 3 ) 2 n ) O(\frac{n^7}{128}\log m+\binom{n}{3}2^n) O(128n7logm+(3n)2n)
其实真正算出来也只有大概 1.7 × 1 0 8 1.7\times 10^8 1.7×108 左右,这也证明了在高次复杂度计算时忽略常数导致的误差是非常非常大的。

tip:写的时候一定注意不要让复杂度退化,特别是后半段的 dp ,std 使用了很巧妙的遍历方式避免了滚动数组,使得常数极大的优化(我写的那种方法一直 TLE 照着标程写才过了的)。

T4- ZZH 的旅行(journey)

改编自:CF932F Escape Through Leaf
突然记得考试前一天晚上我奶过今天要考线段树合并。
怎么说呢,可能算是李超线段树合并模板题吧。
首先写出转移方程式:
f x = max ⁡ { 0 , max ⁡ v ∈ s u b t r e e   o f   x { f v + b v ( a x + d e p x − d e p v ) } } f_x=\max\{0,\max_{v \in subtree\ of\ x}\{f_v+b_v(a_x+dep_x-dep_v)\}\} fx=max{0,vsubtree of xmax{fv+bv(ax+depxdepv)}}
考虑这个方程本质上是一个 y = b v x + f v − b v × d e p v y=b_vx+f_v-b_v\times dep_v y=bvx+fvbv×depv 的直线,其中 x = a x + d e p x x=a_x+dep_x x=ax+depx
而查直线集合某个点的最值我们可以运用李超线段树或者线段树套凸包来维护,这里我们采用相对比较简单的李超线段树。
因此在本题中我们只需要一个动态开点且支持合并的线段树即可。
插入和查询操作和普通李超线段数没有什么区别,合并的时候我们只需要暴力先合并结构,再把丢弃的节点的信息暴力插入新的节点即可。
复杂度证明可以看这里
复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)

tip:这题对于线段树合并来说空间限制有些紧张,但是注意到动态开点李超线段树实际上只需要 O ( n ) O(n) O(n) 的空间,这样就能够避免 MLE 了。

Part 3 实现

T1-game

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;

#define LL long long
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 1000000
#define MOD 998244353
#define INF 1000000000
#define mem(x,v) memset(x,v,sizeof(x))

namespace IO{
	const int sz=1<<22;
	char a[sz+5],b[sz+5],*p1=a,*p2=a,*t=b,p[105];
	inline char gc(){
		return p1==p2?(p2=(p1=a)+fread(a,1,sz,stdin),p1==p2?EOF:*p1++):*p1++;
	}
	template<class T> void read(T& x){
		x=0; char c=gc();
		for(;c<'0'||c>'9';c=gc());
		for(;c>='0'&&c<='9';c=gc())
			x=x*10+(c-'0');
	}
	inline void flush(){fwrite(b,1,t-b,stdout),t=b; }
	inline void pc(char x){*t++=x; if(t-b==sz) flush(); }
	template<class T> void pi(T x,char c='\n'){
		if(x==0) pc('0'); int t=0;
		for(;x;x/=10) p[++t]=x%10+'0';
		for(;t;--t) pc(p[t]); pc(c);
	}
	struct F{~F(){flush();}}f; 
}
using IO::read;
using IO::pi;

int T,n,s,t;
vector<int> P[MAXN+5],G[MAXN+5];

struct binset{
	int fa[MAXN+5],mn[MAXN+5];
	void init(){for(int i=1;i<=n;i++)fa[i]=mn[i]=i;}
	int xfind(int x){return (fa[x]==x)?x:fa[x]=xfind(fa[x]);}
	void bin(int u,int v){
		int a=xfind(u),b=xfind(v);
		fa[b]=a,mn[a]=min(mn[a],mn[b]);
	}
}A,B;

bool check(int mx){
	A.init(),B.init();
	int u=s,v=t,preu,prev;
	int lsu=0,lsv=0;
	while(1){
		preu=u,prev=v;
		for(int i=lsv+1;i<=mx-u;i++)
			for(int j=0;j<G[i].size();j++)
			B.bin(i,G[i][j]);
		lsv=max(lsv,mx-u),v=B.mn[B.xfind(t)];
		for(int i=lsu+1;i<=mx-v;i++)
			for(int j=0;j<P[i].size();j++)
			A.bin(i,P[i][j]);
		lsu=max(lsu,mx-v),u=A.mn[A.xfind(s)];
		if(u==1&&v==1)break;
		if(preu==u&&prev==v)return 0;
	}
	return 1;
}

int main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	read(T);
	while(T--){
		read(n);
		for(int i=1;i<n;i++){
			int u,v;
			read(u),read(v);
			if(u<v)swap(u,v);
			P[u].push_back(v);
		}
		for(int i=1;i<n;i++){
			int u,v;
			read(u),read(v);
			if(u<v)swap(u,v);
			G[u].push_back(v);
		}
		read(s),read(t);
		int l=s+t,r=2*n,ans=-1;
		while(l<=r){
			int mid=(l+r)>>1;
			if(check(mid))ans=mid,r=mid-1;
			else l=mid+1;
		}
		pi(ans);
		for(int i=1;i<=n;i++)
		P[i].clear(),G[i].clear();
	}
}

T2-knapsack

算法1

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;

#define LL long long
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXM 40 
#define MAXN (1<<20)
#define MAXP (1<<20)
#define MAXQ 1000
#define MOD 998244353
#define INF 1000000000
#define mem(x,v) memset(x,v,sizeof(x))

namespace IO{
	const int sz=1<<22;
	char a[sz+5],b[sz+5],*p1=a,*p2=a,*t=b,p[105];
	inline char gc(){
		return p1==p2?(p2=(p1=a)+fread(a,1,sz,stdin),p1==p2?EOF:*p1++):*p1++;
	}
	template<class T> void read(T& x){
		x=0; char c=gc();
		for(;c<'0'||c>'9';c=gc());
		for(;c>='0'&&c<='9';c=gc())
			x=x*10+(c-'0');
	}
	inline void flush(){fwrite(b,1,t-b,stdout),t=b; }
	inline void pc(char x){*t++=x; if(t-b==sz) flush(); }
	template<class T> void pi(T x,char c='\n'){
		if(x==0) pc('0'); int t=0;
		for(;x;x/=10) p[++t]=x%10+'0';
		for(;t;--t) pc(p[t]); pc(c);
	}
	struct F{~F(){flush();}}f; 
}
using IO::read;
using IO::pi;

int n,q,na,nb,cnta,cntb,mp[MAXN+5];
LL v[MAXM+5],a[MAXN+5],b[MAXP+5],ans[MAXQ+5];
struct Query{
	int id,op;LL val;
}Q[MAXQ+5];

bool cmp(Query s1,Query s2){return s1.val>s2.val;} 

int main(){
	freopen("knapsack.in","r",stdin);
	freopen("knapsack.out","w",stdout);
	read(n),read(q);
	for(int i=1;i<=n;i++)read(v[i]);
	na=n>>1,nb=n-na;
	cnta=(1<<na),cntb=(1<<nb);
	for(int S=0;S<(1<<na);S++){
		LL sum=0;
		for(int i=0;i<na;i++)
		if(S&(1<<i))sum+=v[i+1];
		a[S+1]=sum;
	}
	for(int S=0;S<(1<<nb);S++){
		LL sum=0;
		for(int i=0;i<nb;i++)
		if(S&(1<<i))sum+=v[i+na+1];
		b[S+1]=sum;
	}
	sort(a+1,a+cnta+1),sort(b+1,b+cntb+1);
	for(int i=1;i<=q;i++){
		LL l,r;read(l),read(r);
		Q[i*2-1]=(Query){i,-1,l-1};
		Q[i*2]=(Query){i,1,r};
	}
	sort(Q+1,Q+2*q+1,cmp);
	//for(int i=1;i<=cnta;i++)mp[i]=cntb;
	for(int p=1;p<=2*q;p++){
		LL x=Q[p].val,res=0;
		for(int i=1,j=cntb;i<=cnta;i++){
			//j=min(j,mp[i]);
			while(j>=1&&a[i]+b[j]>x)j--;
			//mp[i]=min(mp[i],j);
			res=res+j;
		}
		ans[Q[p].id]+=Q[p].op*res;
	}
	for(int i=1;i<=q;i++)pi(ans[i]);
}

算法2

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;

#define LL long long
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 40
#define MAXQ 2000
#define MOD 998244353
#define INF 2000000000000000
#define mem(x,v) memset(x,v,sizeof(x))

namespace IO{
	const int sz=1<<22;
	char a[sz+5],b[sz+5],*p1=a,*p2=a,*t=b,p[105];
	inline char gc(){
		return p1==p2?(p2=(p1=a)+fread(a,1,sz,stdin),p1==p2?EOF:*p1++):*p1++;
	}
	template<class T> void read(T& x){
		x=0; char c=gc();
		for(;c<'0'||c>'9';c=gc());
		for(;c>='0'&&c<='9';c=gc())
			x=x*10+(c-'0');
	}
	inline void flush(){fwrite(b,1,t-b,stdout),t=b; }
	inline void pc(char x){*t++=x; if(t-b==sz) flush(); }
	template<class T> void pi(T x,char c='\n'){
		if(x==0) pc('0'); int t=0;
		for(;x;x/=10) p[++t]=x%10+'0';
		for(;t;--t) pc(p[t]); pc(c);
	}
	struct F{~F(){flush();}}f; 
}
using IO::read;
using IO::pi;

int n,q,lsz,rsz,L,R;LL v[MAXN+5],ans[MAXQ+5];
vector<LL> P,G;
int ls,rs;

vector<LL> merge(vector<LL> f,LL dt){
	vector<LL> tmp;
	lsz=rsz=f.size();L=R=0;
	while(L<lsz||R<rsz){
		if(L==lsz)tmp.push_back(f[R]+dt),R++;
		else if(R==rsz||f[L]<=f[R]+dt)tmp.push_back(f[L]),L++;
		else tmp.push_back(f[R]+dt),R++;
	}
	return tmp;
}

int main(){
	freopen("knapsack.in","r",stdin);
	freopen("knapsack.out","w",stdout);
	read(n),read(q);
	ls=min(n,15),rs=n-ls;
	for(int i=1;i<=n;i++)read(v[i]);
	for(int i=1;i<=q;i++){
		LL l,r;
		read(l),read(r);
		P.push_back((l-1)*MAXQ+i*2);
		P.push_back(r*MAXQ+i*2+1);
	}
	sort(P.begin(),P.end()); 
	for(int i=1;i<=ls;i++)P=merge(P,-v[i]*MAXQ);
	G.push_back(0);
	for(int i=ls+1;i<=n;i++)G=merge(G,v[i]*MAXQ);
	lsz=P.size(),rsz=G.size();L=R=0;
	while(L<lsz||R<rsz){
		if(L==lsz)R++;
		else if(R==rsz||P[L]<=G[R]){
			int id=(P[L]+INF)%MAXQ;
			ans[id/2]+=(id&1?1:-1)*R;
			L++;
		}else R++;
	}
	for(int i=1;i<=q;i++)pi(ans[i]);
}

T3-counting

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;

#define LL long long
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 300
#define MAXM 200000
#define MOD 998244353
#define INF 1000000000
#define mem(x,v) memset(x,v,sizeof(x))

namespace IO{
	const int sz=1<<22;
	char a[sz+5],b[sz+5],*p1=a,*p2=a,*t=b,p[105];
	inline char gc(){
		return p1==p2?(p2=(p1=a)+fread(a,1,sz,stdin),p1==p2?EOF:*p1++):*p1++;
	}
	template<class T> void read(T& x){
		x=0; char c=gc();
		for(;c<'0'||c>'9';c=gc());
		for(;c>='0'&&c<='9';c=gc())
			x=x*10+(c-'0');
	}
	inline void flush(){fwrite(b,1,t-b,stdout),t=b; }
	inline void pc(char x){*t++=x; if(t-b==sz) flush(); }
	template<class T> void pi(T x,char c='\n'){
		if(x==0) pc('0'); int t=0;
		for(;x;x/=10) p[++t]=x%10+'0';
		for(;t;--t) pc(p[t]); pc(c);
	}
	struct F{~F(){flush();}}f; 
}
using IO::read;
using IO::pi;

int add(int a,int b){return (a+b>=MOD)?a+b-MOD:a+b;}
int dec(int a,int b){return (a-b<0)?a-b+MOD:a-b;}
int mul(int a,int b){return (1LL*a*b)%MOD;}
int fst_pow(int a,int b){
	int res=1;
	while(b){
		if(b&1)res=mul(res,a);
		a=mul(a,a),b>>=1;
	}return res;
}
void upd(int &a,int b){a=add(a,b);}

int n,m,a,b,tot,w,h,inv2,p;
int fac[MAXN+5],ifac[MAXN+5],v[MAXM+5];
int f[MAXM+5][20][20];

int id(int x,int y){return x*(h+1)+y;}
struct Matrix{
	int mat[MAXN+5][MAXN+5];
	Matrix(){mem(mat,0);} 
	Matrix operator*(const Matrix &B)const{
		Matrix C;
		for(int k=0;k<tot;k++)
			for(int i=0;i<tot;i++)
				for(int j=0;j<tot;j++)
				C.mat[i][j]=add(C.mat[i][j],mul(mat[i][k],B.mat[k][j]));
		return C;
	}
}tmp;

Matrix fst_pow(Matrix A,int b){
	Matrix res;
	for(int S=0;S<tot;S++)res.mat[S][S]=1;
	while(b){
		if(b&1)res=res*A;
		A=A*A,b>>=1;
	}return res;
}

void prepare(){
	fac[0]=1;
	for(int i=1;i<=MAXN;i++)fac[i]=mul(fac[i-1],i);
	ifac[MAXN]=fst_pow(fac[MAXN],MOD-2);
	for(int i=MAXN;i>=1;i--)ifac[i-1]=mul(ifac[i],i);
}
int Comb(int a,int b){return mul(fac[a],mul(ifac[a-b],ifac[b]));}
int iComb(int a,int b){return mul(ifac[a],mul(fac[a-b],fac[b]));}

int lowbit(int x){return x&(-x);}
int bcnt(int S){
	int res=0;
	while(S)S-=lowbit(S),res++;
	return res;
}

int main(){
	freopen("counting.in","r",stdin);
	freopen("counting.out","w",stdout);
	read(n),read(m),read(a),read(b);
	for(int S=0;S<(1<<n);S++)read(v[S]);
	p=mul(a,fst_pow(b,MOD-2)),inv2=(MOD+1)>>1;
	prepare();
	for(int i=0;i<=n;i++){
		mem(tmp.mat,0);
		w=i,h=n-i,tot=id(w,h)+1;
		for(int px=0;px<=w;px++)//1->0
			for(int py=0;py<=h;py++){//0->1
				int p1=fst_pow(inv2,w-px+py),p2=fst_pow(inv2,px+h-py);
				for(int tx=px;tx<=w;tx++)
					for(int ty=0;ty<=py;ty++)
					upd(tmp.mat[id(px,py)][id(tx,ty)],mul(mul(p,p1),mul(Comb(w-px,tx-px),Comb(py,ty))));
				for(int tx=0;tx<=px;tx++)
					for(int ty=py;ty<=h;ty++)
					upd(tmp.mat[id(px,py)][id(tx,ty)],mul(mul(dec(1,p),p2),mul(Comb(px,tx),Comb(h-py,ty-py))));
			}
		tmp=fst_pow(tmp,m);
		for(int S=0;S<(1<<n);S++)
		if(bcnt(S)==i)
			for(int px=0;px<=w;px++)
				for(int py=0;py<=h;py++){
					int fp=mul(tmp.mat[id(0,0)][id(px,py)],mul(iComb(w,px),iComb(h,py)));
					f[S][px][py]=mul(v[S],fp);
				}
	}
	for(int L=1,i=n;L<(1<<n);L<<=1,i--)
		for(int st=0;st<(1<<n);st+=(L<<1))
			for(int S=st;S<st+L;S++)
				for(int j=0;j<=i;j++)
					for(int k=0;k<=i-j;k++){
						if(j)upd(f[S][j-1][k],f[S|L][j][k]);
						if(k)upd(f[S|L][j][k-1],f[S][j][k]);
					}
	for(int S=0;S<(1<<n);S++)pi(f[S][0][0],' ');
}

T4-journey

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
using namespace std;

#define LL long long
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 1000000
#define MOD 998244353
#define INF 1000000000
#define mem(x,v) memset(x,v,sizeof(x))

LL read(){
	LL x=0,F=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*F;
}

int n,a[MAXN+5],b[MAXN+5],pos[MAXN+5];
int rt[MAXN+5];
vector<Pr> G[MAXN+5];
LL dep[MAXN+5],dp[MAXN+5],val[MAXN+5];

struct Line{
	LL k,b;
}p[(MAXN<<1)+5];
struct Seg_tree{
	#define mid (l+r>>1)
	int ch[(MAXN<<1)+5][2];
	LL calc(int x,int id){return p[id].k*val[x]+p[id].b;}
	void Insert(int &x,int l,int r,int id){
		if(!x){ch[x=id][0]=ch[id][1]=0;return ;}
		if(calc(mid,x)<calc(mid,id))swap(p[x],p[id]);
		if(l==r)return ;
		if(p[id].k<=p[x].k)Insert(ch[x][0],l,mid,id);
		else Insert(ch[x][1],mid+1,r,id);
	}
	LL Query(int x,int l,int r,int p){
		if(!x)return 0;
		LL nv=calc(p,x);
		if(p<=mid)return max(nv,Query(ch[x][0],l,mid,p));
		else return max(nv,Query(ch[x][1],mid+1,r,p));
	}
	int merge(int x,int y,int l,int r){
		if(!x||!y)return x+y;
		ch[x][0]=merge(ch[x][0],ch[y][0],l,mid);
		ch[x][1]=merge(ch[x][1],ch[y][1],mid+1,r);
		if(calc(mid,x)<calc(mid,y))swap(p[x],p[y]);
		if(l!=r)Insert(x,l,r,y);
		return x;
	}
}T;

void dfs(int x,int fa){
	for(int i=0;i<G[x].size();i++){
		int v=G[x][i].X,wi=G[x][i].Y;
		if(v==fa)continue;
		dep[v]=dep[x]+wi;
		dfs(v,x);
	}
}
void dfs2(int x,int fa){
	for(int i=0;i<G[x].size();i++){
		int v=G[x][i].X;
		if(v==fa)continue;
		dfs2(v,x);
		rt[x]=T.merge(rt[x],rt[v],1,n);
	}
	dp[x]=max(0LL,T.Query(rt[x],1,n,pos[x]));
	p[x]=(Line){b[x],dp[x]-1LL*b[x]*dep[x]};
	T.Insert(rt[x],1,n,x);
}

int main(){
	freopen("journey.in","r",stdin);
	freopen("journey.out","w",stdout); 
	n=read();
	for(int i=1;i<=n;i++)a[i]=read(),b[i]=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read(),d=read();
		G[u].push_back(Pr(v,d));
		G[v].push_back(Pr(u,d));
	}
	dfs(1,0);
	for(int i=1;i<=n;i++)val[i]=dep[i]+a[i];
	sort(val+1,val+n+1);
	for(int i=1;i<=n;i++)pos[i]=lower_bound(val+1,val+n+1,dep[i]+a[i])-val;
	dfs2(1,0);
	for(int i=1;i<=n;i++)printf("%lld\n",dp[i]);
}

Part 4 resource

T1

p1
p2
p3

T2

p4
p5

T3

p6
p7
p8
p9

T4

p10
p11
p12

  • 0
    点赞
  • 0
    收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:游动-白 设计师:我叫白小胖 返回首页
评论
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值