191024-CSP模拟

今天又差点垫底,下次要加油啊。

T1:tom

传送门

这道题,其实转化过来,就是2个连通块,一个为a,一个为b,且a和-b要连通,我们需要找到树上的一条边,将树分为大小为a,b的两部分,对两部分分别染色即可,于是我们先dfs一遍,找到每个点的子树大小,再dfs边找它上面的点个数,若遍历到一个子树大小为a或b,就从它开始,它的父节点为-b或a,再遍历这棵子树,标上序号;若无子树为a或b,就开始遍历,若有一点上面的点个数为a或b,就开始标记。

代码:

#include<bits/stdc++.h>
#define ll long long
#define db double
#define re register
#define cs const
#define N 100005
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch;
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}
bool mark;
int n,a,b,u,v,cnt,tot;
int first[N],net[2*N],to[2*N],size[N],up[N],num[N];
void add(int x,int y)
{
	net[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
}
void dfs1(int x,int fa)
{
	for(int e=first[x];e;e=net[e])
	{
		int v=to[e];
		if(v==fa)	continue;
		dfs1(v,x);
		size[x]+=size[v];
	}
	size[x]++;
}
void make(int fa,int o,int bj)
{
	for(int e=first[o];e;e=net[e])
	{
		int v=to[e];
		if(v==fa)	continue;
		make(o,v,bj);
	}
	if(bj)	num[o]=++cnt;
	else num[o]=--cnt;
}
void dfs2(int x,int fa)
{
	if(mark)	return;
	up[x]=size[fa]-size[x]+up[fa];
	if(x==1)	up[x]=0;
	if(up[x]==a)
	{
		make(x,fa,1);
		cnt=0;
		for(int e=first[x];e;e=net[e])
		{
			int v=to[e];
			if(v==fa)continue;
			make(x,v,0);
		}
		mark=1;
		num[x]=--cnt;
		return;
	}
	if(up[x]==b)
	{
		make(x,fa,0);
		cnt=0;
		for(int e=first[x];e;e=net[e])
		{
			int v=to[e];
			if(v==fa)continue;
			make(x,v,1);
		}
		mark=1;
		num[x]=++cnt;
		return;
	}
	for(int e=first[x];e;e=net[e])
	{
		int v=to[e];
		if(v==fa)	continue;
		if(size[v]==a)
		{
			make(x,v,1);
			cnt=0;
			for(int ee=first[x];ee;ee=net[ee])
			{
				int vv=to[ee];
				if(vv==v)continue;
				make(x,vv,0);
			}
			mark=1;
			num[x]=--cnt;
			return;
		}
		if(size[v]==b)
		{
			make(x,v,0);
			cnt=0;
			for(int ee=first[x];ee;ee=net[ee])
			{
				int vv=to[ee];
				if(vv==v)continue;
				make(x,vv,1);
			}
			mark=1;
			num[x]=++cnt;
			return;
		}
		dfs2(v,x);
		if(mark)	return;
	}
}
int main()
{
	n=read();
	a=read();
	b=read();
	for(int i=1;i<n;i++)
	{
		u=read();
		v=read();
		add(u,v);
		add(v,u);
	}
	dfs1(1,0);
	dfs2(1,0);
	if(!mark)	printf("-1");
	else
	{
		for(int i=1;i<=n;i++)	printf("%d ",num[i]);
	}
}

T2:jerry

传送门

我们可以很容易地得出一个结论,即,括号加在加号前无任何贡献。所以我们可以把连着的正数都加在一起,使整个数列转化为正负负负正负负正负负负负负…。

接着我们预处理下后缀的绝对值和,从后往前dp。

接下来讨论

1.a[i]>0不管

2.a[i]<0

①:a[i+1]<0负负,除了当前是负直接加sufsum(i+1);

②:a[i+1]>0负正负。两种选择:不加,或者加了负的这两个,获得后面的sufsum(i+2)。
详细论述见wyh巨佬链接,因为我实在口胡不下去了

代码:

#include<bits/stdc++.h>
#define int long long
#define db double
#define re register
#define cs const
#define N 200005
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch;
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}
int sum[N],a[N],b[N],f[N];
int cnt,n,t;
signed main()
{
	t=read();
	while(t--)
	{
		cnt=0;
		n=read();
		a[0]=-1;
		for(re int i=1;i<=n;i++)
		{
			f[i]=0;
			a[i]=read();
		}
		for(re int i=1;i<=n;i++)
		{
			if(a[i]<0)	b[++cnt]=a[i];
			else if(a[i-1]<0)	b[++cnt]=a[i];
			else b[cnt]+=a[i];
		}
		sum[cnt+1]=0;	
		for(re int i=cnt;i>=1;i--)	sum[i]=sum[i+1]+abs(b[i]);
		f[cnt]=b[cnt];
		for(re int i=cnt-1;i>=1;i--)
		{
			if(b[i]>=0)	f[i]=f[i+1]+b[i];
			else
			{
				if(b[i+1]<0)	f[i]=b[i]+sum[i+1];
				else	f[i]=max(f[i+1]+b[i],sum[i+2]+b[i]-b[i+1]);
			}	
		}
		printf("%lld\n",f[1]);	
	}
}

T3:speike

传送门

可以猜想出两个结论,一,我们不会走回头路,会一直沿着x轴正方向前进,二,右边的那条边没用,我们可以删去,且我们总是会沿着矩形边界走,接下来我们就可用扫描线(我还不会 )解决问题。

代码(来自wyh巨佬):

//感性理解:沿着线段走总是没错的,,反正只需要算y轴的贡献。总不能横着走回去。
//线段sort,把起点的假线段放在第一个。然后动态规划。 
#include<bits/stdc++.h>
using namespace std;
#define in read()
#define re register
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;
		ch=getchar();
	}return cnt*f;
}
struct node{
	int l,r,tag,key;
}t[5000003];
int pre[1000003][2];
int f[1000003][2];
int n,end;
struct Line{
	int down,up,x;
}line[1000003];
int lcnt;
int bb[4000003],bcnt,len;
bool linecm(Line a,Line b){
	if(a.x!=b.x)return a.x<b.x;
	if(a.down!=b.down)return a.down<b.down;
	return a.up<b.up;
}
inline void build(int u,int l,int r){
	t[u].l=l;t[u].r=r;
	if(l==r){
		return;
	}int mid=(l+r)>>1;
	build(u*2,l,mid);build(u*2+1,mid+1,r);
}
inline void pushdown(int u){
	if(t[u].tag){
		t[u*2].tag=t[u*2+1].tag=t[u].tag;
		t[u*2].key=t[u*2+1].key=t[u].tag;
		t[u].tag=0;
	}
}
inline void modify(int u,int ql,int qr,int key){
	if(t[u].r<ql||t[u].l>qr)return;
	if(ql<=t[u].l&&t[u].r<=qr){
		t[u].tag=t[u].key=key;return;
	}pushdown(u);
	modify(u*2,ql,qr,key);modify(u*2+1,ql,qr,key);
}
inline int query(int u,int pos){
	if(t[u].l==t[u].r&&t[u].r==pos)return t[u].key;
	pushdown(u);
	int mid=(t[u].l+t[u].r)>>1;
	if(pos<=mid)return query(u*2,pos);else return query(u*2+1,pos);
}
signed main(){
	memset(f,0x3f,sizeof(f));
	n=in;
	end=in;
	line[++lcnt]={0,0,0};
	line[++lcnt]={0,0,end};
	for(re int i=1;i<=n;i++){
		int a=in;int b=in;int c=in;int d=in;
		if(b<d)swap(b,d);bb[++bcnt]=b;bb[++bcnt]=d;
		line[++lcnt]={d,b,a};
	}
	bb[++bcnt]=0;
	sort(line+1,line+lcnt+1,linecm);
	sort(bb+1,bb+bcnt+1);
	len=unique(bb+1,bb+bcnt+1)-bb-1;
	for(re int i=1;i<=lcnt;i++){
		line[i].down=lower_bound(bb+1,bb+len+1,line[i].down)-bb;
		line[i].up=lower_bound(bb+1,bb+len+1,line[i].up)-bb;
	}
	for(re int i=2;i<=lcnt;i++){
		if(line[i].x)break;if(!bb[line[i].down]&&!bb[line[i].up]){swap(line[i],line[1]);break;}
	}
	build(1,1,len);
	for(re int i=1;i<=lcnt;i++){
		pre[i][0]=query(1,line[i].down);pre[i][1]=query(1,line[i].up);
		modify(1,line[i].down,line[i].up,i);
	}
	f[1][0]=f[1][1]=0;
	for(re int i=2;i<=lcnt;i++){
		int p=max(1,pre[i][0]);
		f[i][0]=min(f[p][0]+abs(bb[line[i].down]-bb[line[p].down]),f[p][1]+abs(bb[line[i].down]-bb[line[p].up]));
		p=max(1,pre[i][1]);
		f[i][1]=min(f[p][0]+abs(bb[line[i].up]-bb[line[p].down]),f[p][1]+abs(bb[line[i].up]-bb[line[p].up]));
	}
	cout<<f[lcnt][0]+end;
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值