[improvement group training 2021] Round2

Posted by gigabyt3r on Fri, 12 Nov 2021 03:07:43 +0100

I'm too lazy to talk nonsense. I'm a fool.

C

Title Description

Given a tree with \ (n \) points, record \ (L(u,v) \) as the number of points on the \ ((u,v) \) simple path. For the path \ ((a,b),(c,d) \) point disjoint Quad \ ((a,b,c,d) \), we want to know how many different values \ ((L(a,b),L(c,d)) \) have.

\(n\leq 5\cdot 10^5\)

solution

The key \ (\ tt observation \) is that for any Quad, there must be an edge so that the two paths are in two subtrees respectively.

Then we can enumerate this edge and take the diameter in both subtrees. It is found that this process can be realized by changing the root \ (dp \), and then we need to make a rectangular area and, in fact, it is a suffix maximum.

summary

This problem has a "no cross" limit. We can enumerate one thing to "cut" them.

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
const int M = 500005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,lf[M],b[M];vector<int> g[M];
struct diameter
{
	int x,y,z;
	diameter() {x=y=z=0;}
	void add(int c)
	{
		if(c>x) z=y,y=x,x=c;
		else if(c>y) z=y,y=c;
		else if(c>z) z=c;
	}
	int len(int ban)
	{
		if(x==ban) return y+z+1;
		if(y==ban) return x+z+1;
		if(z==ban) return x+y+1;
		return x+y+1;
	}
}dp[M];
void upd(int &x,int y) {x=max(x,y);}
void work(int u,int fa)
{
	for(auto v:g[u]) if(v^fa)
		work(v,u),dp[u].add(dp[v].x+1);
}
void dfs(int u,int fa,int mx)
{
	dp[u].add(lf[u]);
	for(auto v:g[u]) if(v^fa)
	{
		int ban=dp[v].x+1,wv=dp[v].len(0);
		int wu=max(dp[u].len(ban),mx);
		upd(b[wu],wv);upd(b[wv],wu);
		lf[v]=(dp[u].x==dp[v].x+1)?dp[u].y+1:dp[u].x+1;
		dfs(v,u,wu);
	}
}
signed main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		g[u].push_back(v);
		g[v].push_back(u);
	}
	work(1,0);dfs(1,0,0);
	long long ans=0;
	for(int i=n;i>=1;i--)
	{
		b[i]=max(b[i],b[i+1]);
		ans+=b[i];
	}
	printf("%lld\n",ans);
}

D

Title Description

There are \ (n+1 \) cities on the same line, starting from \ (0 \) from left to right. The distance between the city \ (I \) and the city \ (0 \) is \ (a_i \). You need to start from the city \ (0 \) to reach the city \ (n \), and you need to eat a sugar every unit of distance.

Each city has a candy store with unlimited sugar. The price of a candy store in the \ (I \) city is \ (b_i \) and the price of selling a candy is \ (s_i \). You can sell excess candy in the store. You can carry up to \ (m \) units of candy at the same time.

At the beginning, you have unlimited money. Ask how much money you need to spend at least to reach the end (it can be negative)

\(n\le 200000\)

Solution 1

The common routine of this kind of trading problem is: we look for some unreasonable equivalent operations, and the skill is often delay.

For this problem, we fill our backpacks in every store. If there are more candy in the end, we will refund them at the original price. According to this basic idea, when we visit the store \ (i \), we are greedy as follows:

  • First, consider selling the existing candy. If the price is \ (x \) and the selling value of the store is \ (y \), then selling it can earn \ (y-x \), but violent selling is not good. Because we may have to eat the candy later, we change its value to \ (y \), so when we refund money later, it is equivalent to choosing to sell the candy, If you eat this candy, it is equivalent to not selling it, which is a clever delay operation.
  • Then consider filling the backpack, replacing some candy with candy that is more expensive than it, and then plug in the current candy.
  • Finally, consider the candy consumed in the next displacement. According to greed, we consume the candy with the lowest price.

According to the operation characteristics, we can maintain it with double ended queue. Each element is a binary of value quantity, and the operation becomes:

  • Pop up some elements with \ (x \) value at the head of the team, and modify their weight to \ (y \)
  • Pop up some elements with higher price than the current one at the end of the team and insert new elements.
  • Delete elements from the head of the team in turn.

According to the sharing principle, time complexity \ (O(n) \)

Solution 2

This problem can also be started from the perspective of \ (dp \), but if you always want to reduce dimension, you will enter a dead end of thinking.

Let \ (dp[i][j] \) represent the minimum cost of candy with \ (j \) when you go to the \ (I \) store. It is not difficult to write the following transfer:

\[dp[i][j]\leftarrow dp[i-1][j-d] \ \ (1) \]

\[dp[i][j]\leftarrow dp[i][j-k]+k\cdot b_i \ \ (2) \]

\[dp[i][j]\leftarrow dp[i][j+k]-k\cdot s_i \ \ (3) \]

In fact \ ((2) (3) \) is the combination of convex functions, and \ ((1) \) is the overall translation of the function, so this is a variant of slope trick. We can prove that \ (dp[i] \) is a convex hull, and the transfer can be translated into the operation on the convex hull as follows:

  • Pop up the front points, and then pan as a whole.
  • For broken lines with a slope greater than \ (b_i \), change their slope to \ (b_i \)
  • For polylines whose front slope is less than \ (s_i \), change their slope to \ (s_i \)

This can be implemented with a double ended queue, and then you find that it is the same as the solution. The time complexity \ (O(n) \)

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 200005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,l,r,a[M],b[M],s[M],qv[M<<1],qn[M<<1];
long long ans;
signed main()
{
	freopen("candy.in","r",stdin);
	freopen("candy.out","w",stdout);
	n=read();m=read();l=n;r=n-1;
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=0;i<n;i++) b[i]=read(),s[i]=read();
	for(int i=0;i<n;i++)
	{
		//sell the candy
		int cnt=0;
		while(l<=r && qv[l]<=s[i]) cnt+=qn[l],l++;
		qn[--l]=cnt;qv[l]=s[i];
		//fulfill the bagpack & abandon the trash
		cnt=(i==0)?m:a[i]-a[i-1];
		while(l<=r && qv[r]>=b[i])
			ans-=1ll*qn[r]*qv[r],cnt+=qn[r],r--;
		qn[++r]=cnt;qv[r]=b[i];
		ans+=1ll*cnt*b[i];
		//use the candy for walking
		cnt=a[i+1]-a[i];
		while(cnt)
		{
			int v=min(cnt,qn[l]);
			cnt-=v;qn[l]-=v;
			if(qn[l]==0) l++;
		}
	}
	for(int i=l;i<=r;i++) ans-=1ll*qn[i]*qv[i];
	printf("%lld\n",ans);
}

Topics: tree dp