分治,倍增

《分治,倍增》

其实全是二分

CF1059E Split the Tree

考虑贪心

为了使链的数量小,肯定是使每条链的长度更长

从叶子开始拓展,对于当前节点,选的儿子一定是能向上拓展最长的

用类似\(LCA\)的倍增

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n,L,S;
int a[MAXN];
int x;
int Fa[MAXN];
vector<int>g[MAXN];
int Res=0;
pair<int,int>Up[MAXN];
int dep[MAXN];
int Sum[MAXN];
int dp[MAXN][25]; 
void dfs(int x)
{
	vector<int>Suit;
	if(!g[x].size())
	{
		Up[x].first=1;
		Up[x].second=a[x];
		if(a[x]>S)
		{
			//printf("?");
			printf("-1");
			exit(0);
		}
		if(x==1)
		{
			Res++;
		}
		return;
	}
	for(int i=0;i<g[x].size();i++)
	{
		int v=g[x][i];
		dep[v]=dep[x]+1;
		Sum[v]=Sum[x]+a[v];
		dp[v][0]=x;
		for(int j=1;j<=20;j++){
			dp[v][j]=dp[dp[v][j-1]][j-1];
		}
		dfs(v);
	
		pair<int,int>sgs=Up[v];
		if(sgs.first+1<=L&&sgs.second+a[x]<=S)
		{
			Suit.push_back(v);
		}
		else
		{
			Res++;
		}
	
	}
	if(!Suit.size())
	{
		Up[x].first=1;
		Up[x].second=a[x];
		if(x==1)
		{
			Res++;		
		}
		if(a[x]>S)
		{
			//printf("?");
			printf("-1");
			exit(0);
		}
		return;
	}
	int Mini=0x3f3f3f3f;
	int idi=0;
	for(int i=0;i<Suit.size();i++)
	{
		int ORG=Suit[i];
		int Now=Suit[i];
		pair<int,int>Nod=Up[Now];
		for(int j=20;j>=0;j--)
		{
			int FF=dp[Now][j];
			if((dep[ORG]-dep[FF])+Nod.first<=L&&((Sum[ORG]-Sum[FF])+Nod.second<=S))
			{
				Now=FF;
			}
		}
		int Wei=(dep[Now]);
		if(Wei<Mini)
		{
			idi=ORG;
			Mini=Wei;
		 } 
	}
	for(int i=0;i<Suit.size();i++)
	{
		int ORG=Suit[i];
		if(ORG==idi)
		{
			pair<int,int>New;
			New.first=Up[idi].first+1;
			New.second=Up[idi].second+a[x];
			Up[x]=New;
			if(x==1)
			{
				Res++;
				
			}
		}
		else
		{
			Res++;
		}
	}
}
signed main()
{
	scanf("%lld %lld %lld",&n,&L,&S);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	} 
	for(int i=2;i<=n;i++)
	{
		scanf("%lld",&x);
		Fa[i]=x;
		g[x].push_back(i);
	}
	dfs(1);
	printf("%lld\n",Res);
}
//3 3 2200000000
//1000000000 1000000000 1000000000
//1 2

CF1394C Boboniu and String

相似就是\(B,N\)的数量相同

那么对于每一个操作,实际上要么是\(|B|,|N|\)单独\(\pm1\),或是共同\(\pm1\)

如果把他放在平面直角坐标系

image

上面的边界即为可达

二分答案\(Mid\)

然后构造一个\(T\)

实际上首先\(T与S_i\)的横纵坐标差的最大值要小于\(Mid\)

从图上开,这样是一个正方形的边框,但实际上是六边形

所以还要加上一个两次函数的限制

\[x-Mid\leq x'\leq x+Mid \\ y-Mid\leq y'\leq y+Mid \\ (x-y)-Mid\leq (x'-y')\leq (x-y)+Mid \]

解出\(x,y,(x-y)\),然后解不等式即可

#include<bits/stdc++.h>
using namespace std;
const int MAXN=3e5+5;
int n;
string s;

int detA[MAXN],detB[MAXN];
int AL,AR,BL,BR,detL,detR,A,B;
bool check(int Mid)
{
	AL=0,AR=1e6,BL=0,BR=1e6;
	detL=-1e6;
	detR=1e6;
	for(int i=1;i<=n;i++)
	{
		AL=max(AL,detA[i]-Mid);
		AR=min(AR,detA[i]+Mid);
		BL=max(BL,detB[i]-Mid);
		BR=min(BR,detB[i]+Mid);
		detL=max(detL,detA[i]-detB[i]-Mid);
		detR=min(detR,detA[i]-detB[i]+Mid);
	}
	if(AL>AR||BL>BR||detL>detR)
	{
	//	printf("%d %d\n",detL,detR);
		return 0;
	}
	int NXL=BL+detL;
	int NXR=BR+detR;
//	printf("?");
	if(NXR<AL||NXL>AR)
	{
		return 0;
	}
	NXL=max(NXL,AL);
	NXR=min(NXR,AR);
	A=NXL;
	int NYL=A-detR;
	int NYR=A-detL;
	NYL=max(NYL,BL);
	NYR=min(NYR,BR);
	B=NYL;
	return 1;
}
//(x,y) max(abs(x-X),abs(y-Y))<=d
//(x-y) 
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		cin>>s;
		for(int j=0;j<s.size();j++)
		{
			if(s[j]=='B')
			{
				detA[i]++;
			}
			else
			{
				detB[i]++;
			}
		}
	//	printf("%d %d\n",detA[i],detB[i]);
	}
	int l=0;
	int r=1e6;
	int key;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid))
		{
			key=mid;
			r=mid-1;
		}
		else
		{
			l=mid+1; 
		}
	 }
	printf("%d\n",key);
	check(key);
	//printf("%d\n",check(12));
	//printf("%d %d %d %d\n",AL,AR,BL,BR);
	for(int i=1;i<=A;i++)
	{
		printf("B");
	}
	for(int i=1;i<=B;i++)
	{
		printf("N");
	}
 } 

CF627D Preorder Test

首先可以二分答案\(Mid\)

当前限制为\(Mid\),权值大于\(Mid\)为能走的节点

我们设\(dp_i\)为以\(i\)为根时能走最多可行点

\[dp_x=1+(dp_v)[dp_v=Siz_v]+Max(dp(v)[dp_v\neq Siz_v]) \]

由于根不是指定的

所以要换根,有点复杂,但思路简单

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
int n,k;
int a[MAXN];
int x,y;
vector<int>g[MAXN];
int dp1[MAXN];
int dp2[MAXN];
int dp_up[MAXN];
int Siz[MAXN];
int Aiv[MAXN];
void dfs1(int x,int f)
{
	Siz[x]=1;
	for(int i=0;i<g[x].size();i++)
	{
		int v=g[x][i];
		if(v==f)
		{
			continue;
		}
		dfs1(v,x);
		Siz[x]=Siz[x]+Siz[v];
	}
	if(!Aiv[x])
	{
		dp1[x]=0;
		return;
	}
	dp1[x]=1;
	int Maxi=0;
	for(int i=0;i<g[x].size();i++)
	{
		int v=g[x][i];
		if(v==f)
		{
			continue;
		}
		if(dp1[v]==Siz[v])
		{
			dp1[x]+=dp1[v];
		}
		else
		{
			Maxi=max(Maxi,dp1[v]);
		}
	}
	dp1[x]+=Maxi;
}
void dfs2(int x,int f)
{
	int Sum=0;
	int Fir=0;
	int Sec=0;
	if(Aiv[x])
	{
		Sum=1;
		for(int i=0;i<g[x].size();i++)
		{
			int v=g[x][i];
			if(v==f)
			{
				continue;
			}
			if(dp1[v]==Siz[v])
			{
				Sum+=dp1[v];
			}
			else
			{
				if(dp1[v]>=Fir)
				{
					Sec=Fir;
					Fir=dp1[v];
				}
				else if(dp1[v]>Sec)
				{
					Sec=dp1[v];
				}
			}
		 
		}
	
		if(dp_up[x]==n-Siz[x])
		{
			Sum+=dp_up[x];
		}
		else
		{
			if(dp_up[x]>=Fir)
			{
				Sec=Fir;
				Fir=dp_up[x];
			}
			else if(dp_up[x]>Sec)
			{
				Sec=dp_up[x];
			}
		}
		dp2[x]=Sum+Fir;
	}
	else
	{
		dp2[x]=0;
	}
	
	for(int i=0;i<g[x].size();i++)
	{
		int v=g[x][i];
		if(v==f)
		{
			continue;
		}
		if(Aiv[x])
		{
			if(dp1[v]==Siz[v])
			{
				dp_up[v]=dp2[x]-dp1[v];
			 }
			 else
			 {
			 	if(dp1[v]==Fir)
			 	{
			 		dp_up[v]=dp2[x]-Fir+Sec;
				 }
				 else
				 {
				 	dp_up[v]=dp2[x];
				 }
			 }
		}
		else
		{
			dp_up[v]=0;	
		}
		
		dfs2(v,x);
	}
 } 
bool check(int mid)
{
	for(int i=1;i<=n;i++)
	{
		if(a[i]>=mid)
		{
			Aiv[i]=1;
		 } 
		 else
		 {
		 	Aiv[i]=0;
		 }
	}
	dfs1(1,0);
	dp2[1]=dp1[1];
	dfs2(1,0);
	int ssMaxi=0;
	for(int i=1;i<=n;i++)
	{
		ssMaxi=max(ssMaxi,dp2[i]);
	}
	return ssMaxi>=k;
}
signed main()
{
	scanf("%d %d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	for(int i=1;i<n;i++)
	{
		scanf("%d %d",&x,&y);
		g[x].push_back(y);
		g[y].push_back(x);
	}
	int l=1;
	int r=1000000;
	int key;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid))
		{
			key=mid;
			l=mid+1;
		}
		else
		{
			r=mid-1;
		}
	} 
	printf("%d\n",key);
} 
posted @ 2022-07-04 07:59  kid_magic  阅读(25)  评论(0编辑  收藏  举报