Codeforces Round #600 (Div. 2) 题解

161 阅读6分钟

比赛链接

A

思路

至多只能操作一次,只需要判断A和B是否只有一段连续区间[l,r][l,r],存在a[i]b[i]=constant(i,j[l,r])a[i]-b[i]=constant(i,j\in [l,r])

代码

#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;
ll a[N],b[N];
void solve()
{
	In(1,&n);
	rep(i,1,n)
		In(1,&a[i]);
	rep(i,1,n)
		In(1,&b[i]);
	ll flag=0,end=0;
	rep(i,1,n)
	{
		if(a[i]<b[i])
		{
			if(end)
			{
				puts("NO");
				return;
			}
			else if(!flag)
				flag=b[i]-a[i];
			else if(b[i]-a[i]!=flag)
			{
				puts("NO");
				return;
			}
		}
		else if(a[i]==b[i] && flag)
			end=1;
		else if(a[i]>b[i])
		{
			puts("NO");
			return;
		}
	}
	puts("YES");
}
int main()
{
	int _;
	cin>>_;
	while(_--)
	{
		solve();
	}
}

B

思路

合法的一天数据总和必定为00,考虑维护当天的数据总和sumsum,一旦sumsum清零就结束这一天。如果sumsum为负数或一个元素在一天当中出现超过一次,则视为不合法的一天,用mapmap维护元素是否出现过,每一天结束都清空mapmap
这道题刚开始考虑的是一旦遇到一个重复出现的元素就检查sumsum是否为00,如果为00,就结束这一天并以这个重复出现的元素作为新一天的第一个元素,否则即视为不合法。这样其实并不正确,因为如果有机会在这个重复出现元素前面结束前一天,就能够保证这个元素合法地加入新的一天。
尽早的结束一天总是合理的,因为它可以允许后面更多的元素加入到新的一天中而不和前一天的元素发生冲突。

代码

#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=1E5+10;
ll n,cnt,sum,lst=1;
ll ans[N];
map<ll,ll> ma;
int main()
{
	In(1,&n);
	ll flag=1;
	rep(i,1,n)
	{
		ll cur;
		In(1,&cur);
		sum+=cur;
		if(sum==0)
		{
			ans[++cnt]=i-lst+1;
			lst=i+1;
			ma.clear();
		}
		else if(sum<0)
		{
			flag=0;
			break;
		}
		else if(cur>0)
		{
			if(ma.find(cur)==ma.end())
				ma.insert(pair<ll,ll>(cur,1));
			else
			{
				flag=0;
				break;
			}
		}
		else if(cur<0 && ma.find(-cur)==ma.end())
		{
			flag=0;
			break;
		}
	}
	if(!flag || sum)
		puts("-1");
	else
	{
		sum=0;
		rep(i,1,cnt)
			sum+=ans[i];
		if(sum!=n)
			ans[++cnt]=n-sum;
		Out(1,cnt);
		rep(i,1,cnt)
			Out_(1,ans[i]);
		puts("");
	}
}

C

思路

感觉比B题要水一点。因为不规定取糖的顺序,因此从小到大取一定是最优的。取完之后怎么分配在哪一天吃呢?代价小的放在后几天吃,代价大的放在前几天吃必然最优,具体证明可以反证。但是每次都从头算肯定会超时,考虑如何优化。
假设我们当前已经知道了第ii天的代价,现在考虑向第i+1i+1天转移。转移的方案必然是所有糖果向后移一位,给新来的代价最大的糖果腾出第一天的第一个位置。那么每一天的第mm个糖果都到了后一天,对应地每个延后到下一天的糖果都会再额外贡献一份代价。也就是说当加入第kk个元素的时候,km,k2m,k3mk-m,k-2m,k-3m\cdots位置上的元素都会对答案产生贡献,只需要维护一下原数组中每隔mm位的前缀和即可在O(1)O(1)的时间内完成转移。

思路

#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=2E5+10;
ll n,m,ans;
ll a[N],sum[N];
int main()
{
	In(2,&n,&m);
	rep(i,1,n)
		In(1,&a[i]);
	sort(a+1,a+n+1);
	rep(i,1,n)
	{
		if(i-m>=1)
			sum[i]=sum[i-m]+a[i];
		else
			sum[i]=a[i];
	}
	ll ans=0;
	rep(i,1,n)
	{
		if(i<=m)
		{
			ans+=a[i];
			Out_(1,ans);
		}
		else
		{
			ans+=sum[i];
			Out_(1,ans);
		}
	}
}

D

思路

所有联通块内的点在数轴上必须是连续的,首先用带权并查集记录下原图中联通块的信息(父节点和结点个数),然后在数轴上扫一遍,如果两个整数所在的联通块没有相连,且前一个联通块在数轴后面还有结点,就合并这两个联通块(比如7和8不属于同一联通块,但是7和9属于同一联通块,就必须合并8所在的联通块和7、9所在的联通块)。合并时要记得维护结点个数。

代码

#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=2E5+10;
ll n,m,tot,ans;
ll head[N],fa[N],vis[N],sum[N];
struct Edge{
	ll nxt,to;
}e[2*N];
inline void add_edge(ll u,ll v,int flag)
{
	e[++tot].nxt=head[u];
	e[tot].to=v;
	head[u]=tot;
	if(flag)
		add_edge(v,u,0);
}
ll find(ll x)
{
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
ll dfs(ll u)
{
	ll flag=0;
	bl(u,i)
	{
		ll v=e[i].to;
		if(vis[v])
			continue;
		flag=1;
		vis[v]=1;
		dfs(v);
		fa[v]=u;
	}
	return flag;
}
void print()
{
	rep(i,1,n)
		Out_(1,sum[i]);
}
int main()
{
	In(2,&n,&m);
	rep(i,1,m)
	{
		ll u,v;
		In(2,&u,&v);
		add_edge(u,v,1);
	}
	rep(i,1,n)
		fa[i]=i;
	rep(i,1,n)
	{
		if(!vis[i])
		{
			vis[i]=1;
			ll tmp=dfs(i);
			if(!tmp)
				vis[i]=0;
		}
	}
	rep(i,1,n)
		sum[find(i)]++;
	rep(i,1,n)
	{
		--sum[find(i)];
		if(sum[find(i)] && find(i+1)!=find(i))
		{
			++ans;
			sum[find(i)]+=sum[find(i+1)];
			fa[find(i+1)]=i;
		}
	}
	Out(1,ans);
}

E

思路

f[x]f[x]为已经覆盖完[1,x][1,x],继续覆盖到mm需要花费的代价。
可以通过暴力的扩展覆盖到mm,因此f[x]f[x]的初始值为mxm-x
除此之外,还可以与后面的区间连接起来,将其作为“跳板”,从而减少代价。因为nn比较小,可以找到所有左端点位于x+1x+1之后的区间,将其左端点延伸到xx(显然将xx向右延申并不如将左端点向左延申更优,因为延伸左端点的同时右端点也向后延伸了),总的代价就是dis=a[i].lx1dis=a[i].l-x-1。此时右端点的位置是min(m,a[i].r+dis)min(m,a[i].r+dis),两者相加维护答案即可。
另外如果xx已经被初始的区间覆盖,就不需要任何延伸操作了,此时f[x]=f[a[i].r]f[x]=f[a[i].r]a[i]a[i]即覆盖xx的区间。此处覆盖指的是当覆盖完[1,x][1,x]之后不需要任何操作就可以直接通过访问f[a[i].r]f[a[i].r]来获得更优解,因此x=a[i].rx=a[i].r的情况并不属于覆盖。另外x=a[i].l1x=a[i].l-1的情况也是可以直接访问f[a[i].r]f[a[i].r]的,详情见代码中的判断条件。

代码

#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("");
}
struct Node{
	ll x,s,l,r;
	bool operator < (const Node &tmp) const
	{
		return l>tmp.l;
	}
}a[85];
ll n,m;
ll f[(ll)1E5+10];
int main()
{
	In(2,&n,&m);
	rep(i,1,n)
	{
		In(2,&a[i].x,&a[i].s);
		a[i].l=max(0ll,a[i].x-a[i].s);
		a[i].r=min(m,a[i].x+a[i].s);
	}
	sort(a+1,a+n+1);
	for(int j=m;j>=0;--j)
	{
		f[j]=m-j;
		rep(i,1,n)
		{
			if(a[i].l<=j+1 && a[i].r>=j+1)
			{
				f[j]=f[a[i].r];
				break;
			}
			if(j<a[i].l)
			{
				ll dis=a[i].l-j-1;
				f[j]=min(f[j],dis+f[min(m,a[i].r+dis)]);
			}
		}
	}
	Out(1,f[0]);
}

F

思路

这题太牛逼了,先放代码

代码

#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=6E5+10;
ll n,m,k,t,tot;
ll fa[N],head[N],vis[N],dis[N],f[N][24],p[N][24],dep[N];
struct Node{
	ll u,d;
	bool operator < (const Node &tmp) const
	{
		return d>tmp.d;
	}
};
struct Edge{
	ll frm,nxt,to,w;
	bool operator < (const Edge &tmp) const
	{
		return w<tmp.w;
	}
}e[N];
priority_queue<Node> q;
inline void add_edge(ll u,ll v,ll w,int flag)
{
	e[++tot].nxt=head[u];
	e[tot].frm=u;
	e[tot].to=v;
	e[tot].w=w;
	head[u]=tot;
	if(flag)
		add_edge(v,u,w,0);
}
void Dij()
{
	rep(i,1,n)
	{
		if(i>k)
			dis[i]=LLM>>1;
		else
			q.push((Node){i,0});
	}
	while(!q.empty())
	{
		Node cur=q.top();
		q.pop();
		ll u=cur.u;
		if(vis[u])
			continue;
		vis[u]=1;
		bl(u,i)
		{
			ll v=e[i].to;
			if(dis[v]>dis[u]+e[i].w)
				q.push((Node){v,dis[v]=dis[u]+e[i].w});
		}
	}
}
ll find(ll x)
{
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
void print()
{
	rep(i,1,n)
		Out(1,dep[i]);
}
void Kru()
{
	rep(i,1,tot)
		e[i].w+=dis[e[i].frm]+dis[e[i].to];
	rep(i,1,n)
	{
		head[i]=0;
		fa[i]=i;
	}
	stable_sort(e+1,e+tot+1);
	ll cnt=0;
	ll tot_=tot;
	tot=0;
	rep(i,1,tot_)
	{
		ll u=e[i].frm,v=e[i].to;
		ll uu=find(u),vv=find(v);
		if(uu==vv)
			continue;
		fa[uu]=vv;
		++cnt;
		add_edge(u,v,e[i].w,1);
		if(cnt==n-1)
			break;
	}
}
void dfs(ll u,ll father)
{
	dep[u]=dep[father]+1;
	rep(i,0,22)
	{
		p[u][i+1]=p[p[u][i]][i];
		f[u][i+1]=max(f[u][i],f[p[u][i]][i]);
	}
	bl(u,i)
	{
		ll v=e[i].to;
		if(v==father)
			continue;
		p[v][0]=u;
		f[v][0]=e[i].w;
		dfs(v,u);
	}
}
ll lca(ll x,ll y)
{
	ll ret=LLm;
	if(dep[x]<dep[y])
		swap(x,y);
	for(int i=22;i>=0;--i)
	{
		if(dep[p[x][i]]>=dep[y])
		{
			ret=max(ret,f[x][i]);
			x=p[x][i];
		}
		if(x==y)
			return ret;
	}
	for(int i=22;i>=0;--i)
	{
		if(p[x][i]!=p[y][i])
		{
			ret=max(ret,f[x][i]);
			ret=max(ret,f[y][i]);
			x=p[x][i];
			y=p[y][i];
		}
	}
	return max(ret,max(f[x][0],f[y][0]));
}
int main()
{
	In(4,&n,&m,&k,&t);
	rep(i,1,m)
	{
		ll u,v,w;
		In(3,&u,&v,&w);
		add_edge(u,v,w,1);
	}
	Dij();
	Kru();
	dfs(1,0);
	while(t--)
	{
		ll x,y;
		In(2,&x,&y);
		Out(1,lca(x,y));
	}
}