Codeforces Round #661 (Div. 3) 题解

88 阅读3分钟

比赛链接
A、B、C、D、E1略

E2

思路

对于代价相同的边肯定取对答案贡献较大的,考虑把两种代价的边分别丢到两个优先队列里,代价为11的称为AA堆,代价为22的称为BB堆。对于两条AA边的贡献之和大于一条BB边的,此时选择这条BB边必定不如选择两条AA边更优,考虑选择贡献最大的一条AA边还是两条都选。如果当前选一条AA边再加一条BB边就可以达成目的,代价为33,那么直接选择两条AA边显然错过了这种情况,因此每次比较只选择一条边。
另外注意一条边被取出之后会重新加入队列,所以取两条AA边的时候有可能是同一条边取两遍。
代码细节较多。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef long long ll;
typedef double db;
using namespace std;
inline void In(ll _,...)
{
	va_list lis;
	va_start(lis,_);
	while(_--)
		scanf("%lld",va_arg(lis,ll*));
}
inline void Out(ll _,...)
{
	va_list lis;
	va_start(lis,_);
	while(_--)
		printf("%lld\n",va_arg(lis,ll));
}
inline void Out_(ll _,...)
{
	va_list lis;
	va_start(lis,_);
	while(_--)
		printf("%lld ",va_arg(lis,ll));
	puts("");
}
const int N=1E5+10;
ll n,s,tot,sum;
ll head[N];
struct Edge{
	ll nxt,to,w,ty;
}e[2*N];
struct Node{
	ll w,cnt;
	ll cut() const
	{
		return cnt*(w-w/2);
	}
	bool operator < (const Node &tmp) const
	{
		return this->cut()<tmp.cut();
	}
};
priority_queue<Node> q1,q2;
inline void add_edge(ll u,ll v,ll w,ll ty,int flag)
{
	e[++tot].nxt=head[u];
	e[tot].to=v;
	e[tot].w=w;
	e[tot].ty=ty;
	head[u]=tot;
	if(flag)
		add_edge(v,u,w,ty,0);
}
ll dfs(ll u,ll fa)
{
	ll flag=0,ret=0;
	bl(u,i)
	{
		ll v=e[i].to;
		if(v==fa)
			continue;
		flag=1;
		ll tmp=dfs(v,u);
		ret+=tmp;
		if(e[i].ty&1)
			q1.push((Node){e[i].w,tmp});
		else
			q2.push((Node){e[i].w,tmp});
		sum+=e[i].w*tmp;
	}
	if(!flag)
		ret=1;
	return ret;
}
void solve()
{
	tot=sum=0;
	while(!q1.empty())
		q1.pop();
	while(!q2.empty())
		q2.pop();
	In(2,&n,&s);
	rep(i,1,n)
	{
		head[i]=0;
		e[i]=e[n+i]=(Edge){0,0,0,0};
	}
	rep(i,1,n-1)
	{
		ll u,v,w,ty;
		In(4,&u,&v,&w,&ty);
		add_edge(u,v,w,ty,1);
	}
	dfs(1,0);
	ll ans=0;
	while(sum>s)
	{
		if(!q1.empty() && sum-q1.top().cut()<=s)
		{
			++ans;
			break;
		}
		Node x=(Node){0,0},y=(Node){0,0},z=(Node){0,0};
		if(!q1.empty())
		{
			x=q1.top();
			q1.pop();
			y=(Node){x.w/2,x.cnt};
			if(!q1.empty() && q1.top().cut()>y.cut())
				y=q1.top();
		}
		if(!q2.empty())
		{
			z=q2.top();
			q2.pop();
		}
		if(x.cut()+y.cut()>=z.cut())
		{
			sum-=x.cut();
			++ans;
			x.w>>=1;
			q1.push(x);
			q2.push(z);
		}
		else
		{
			sum-=z.cut();
			ans+=2;
			z.w>>=1;
			q2.push(z);
			if(x.w)
				q1.push(x);
		}
	}
	Out(1,ans);
}
int main()
{
	int _;
	cin>>_;
	while(_--)
	{
		solve();
	}
}

F

思路

首先对端点离散化。区间dp,设f[i][j]f[i][j]为区间[i,j][i,j]上的答案,显然它直接包含较短区间的答案,即f[i][j]=max(f[i+1][j],f[i][j1]f[i][j]=max(f[i+1][j],f[i][j-1]。考虑怎么通过新的端点获得更优的答案。一条线段只有端点与ii重合时才可以贡献答案,假设有一条左端点在ii的线段,其右端点为pp,就有f[i][j]=max(f[i][p]+f[p+1][j])f[i][j]=max(f[i][p]+f[p+1][j])。预处理一下以某个端点作为左端点的线段有哪些即可。

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int (i)=(st);(i)<=(ed);++(i))
#define bl(u,i) for(int (i)=head[(u)];(i);(i)=e[(i)].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef long long ll;
typedef double db;
using namespace std;
inline void In(ll _,...)
{
	va_list lis;
	va_start(lis,_);
	while(_--)
		scanf("%lld",va_arg(lis,ll*));
}
inline void Out(ll _,...)
{
	va_list lis;
	va_start(lis,_);
	while(_--)
		printf("%lld\n",va_arg(lis,ll));
}
inline void Out_(ll _,...)
{
	va_list lis;
	va_start(lis,_);
	while(_--)
		printf("%lld ",va_arg(lis,ll));
}
const int N=3005;
int n,tot;
int ori[2*N],l[N],r[N],f[2*N][2*N];
vector<int> ve[2*N];
void print()
{
	rep(i,1,n)
		cout<<l[i]<<" "<<r[i]<<endl;
}
void solve()
{
	tot=0;
	scanf("%d",&n);
	rep(i,1,n)
	{
		scanf("%d%d",l+i,r+i);
		ori[++tot]=l[i];
		ori[++tot]=r[i];
	}
	sort(ori+1,ori+tot+1);
	ll m=unique(ori,ori+tot+1)-ori-1;
	rep(i,1,m)
		rep(j,1,m)
			f[i][j]=0;
	rep(i,1,m)
		ve[i].clear();
	rep(i,1,n)
	{
		l[i]=lower_bound(ori+1,ori+m+1,l[i])-ori;
		r[i]=lower_bound(ori+1,ori+m+1,r[i])-ori;
		f[l[i]][r[i]]=1;
		ve[l[i]].push_back(r[i]);
	}
	rep(len,1,m)
	{
		for(ll i=1;i+len-1<=m;++i)
		{
			ll j=i+len-1;
			ll tmp=f[i][j];
			f[i][j]=0;
			f[i][j]=max(f[i+1][j],f[i][j-1]);
			for(auto k:ve[i])
			{
				if(k>j)
					continue;
				f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);
			}
			f[i][j]+=tmp;
		}
	}
	printf("%d\n",f[1][m]);
}
int main()
{
	int _;
	cin>>_;
	while(_--)
	{
		solve();
	}
}