2020-2021 ACM-ICPC, Asia Seoul Regional Contest 不完整题解与训练赛复盘

RT,训练赛复盘+题解,赛中过8题,比理论上界少1题(
总体来说发挥平稳。
开始复盘吧。 (按照时间顺序来的)

B是啥东西不知道,卢姥爷一发就过去了。

J听ghj说是个开关灯问题,线性基,01矩阵求逆,高斯消元,他也是一发就过去了。

E是个简单DP,问题转化一下,变成两条长链,无向边,给边定向,求对应点入度差能否为d数组。
考虑状态为dp[loc][val1][val2],即,处理完前loc条边,对于第loc+1对点,上面那个点入度val1,下面那个点入度val2,考虑合法转移,一定是使得第loc+1对点满足d数组的,然后一发就过去了。

H题听ghj说是个FFT裸题,上面两个数组卷起来下标除二,对着中间数组判一判。他也是一发就过去了。

C题:读题不规范,本来卢姥爷搞到了正解,我听他复述了一波题意之后发现是个对于每个关键点找最近点HHHHHH,然后不出所料Wa3。
改正之后就是个虚树上面的所有点,随便搞搞就行了。没有采用dfs序建虚树,每次抓一个最深的点,大力跳father就好了,证一证可以发现复杂度是对的。

G题:一波猛卡,然后我读题假了,没发现那个标号的限制,这波是我的锅,而且我还口胡了个三分HHHH这波是死透
考虑如果没有标号限制,那么固定了最左端的点的位置之后,就变成了取个min的事儿。
考虑每个点需要走的路程与最左端那个点的位置的关系。一定是一堆‘V’形的并,则一头一尾取一下就好。
由于要固定顺序,那么顺序有两种,正序和反序,然后枚举1号点在左在右就行了。
注意此题有坑,如果你用double除二,那么在LL范围内,是有东西可以让你在第一位小数出精度误差的。这个很要命,所以特判掉那个除二。

I题:
注意到子串一共只有2e6个,询问2e5。
考虑那个奇妙的限制,比U小的列入考虑范围,比U大的的不予考虑。
有没有点时间轴的味道?如果觉得有,那就对了。
大力出所有子串的子串和,离线下来所有询问,把询问的U与子串的和作为时间轴,转化为:支持插入,二维前缀max的问题。
摸出二维BIT,切之。(YYSY我头一次知道BIT还能二维)

A题:
题目保证,交点一定是十字路口,那就好办不少了。
我们考虑人在线里走。那么,首先处理所有的交点,然后把他们和端点一块儿,扔进node里面,记录下每个点是交点还是端点。
对node集合一顿操作,处理出每个点的上下左右第一个点分别是什么。
人记录当前点和行进方向,如果当期点是一个四联通点,那么走了之后要左转,如果是个路径端点,那么要回头。
挺长,但是不难写。
写完之后改了个CE就过了,这波是状态在线。
贴个代码吧,这东西口胡起来挺抽象的。

#include<bits/stdc++.h>
using namespace std;
int n,T;
int x1[510],y1[510],x2[510],y2[510];
int pcnt;
//0 = U; 1 = L; 2 = D; 3 = R;
int neig[300010][4];//ori中,第loc号点在dir方向上的邻居

struct node { 
	int x,y,pos,type;
};

node ori[300010], sth[300010];

int gdist(int a,int b) { 
	return abs(ori[a].x - ori[b].x) + abs(ori[a].y - ori[b].y);
} 

bool cmp1(node &a,node &b) { 
	if(a.x == b.x) 
		return a.y < b.y;
	else
		return a.x < b.x;
} 

bool cmp2(node &a,node &b) { 
	if(a.y == b.y) 
		return a.x < b.x;
	else
		return a.y < b.y;
} 


int main() { 
	scanf("%d%d",&n,&T);
	for(int i=1;i<=n;++i) { 
		scanf("%d%d%d%d",&x1[i],&y1[i],&x2[i],&y2[i]);
		ori[++pcnt] = {x1[i], y1[i], pcnt, 1};
		ori[++pcnt] = {x2[i], y2[i], pcnt, 1};
	} 
	for(int i=1;i<=n;++i) { 
		if(x1[i] != x2[i]) 
			continue;
		for(int j=1;j<=n;++j) { 
			if(y1[j] != y2[j]) 
				continue;
			int ymi = min(y1[i], y2[i]);
			int yma = max(y1[i], y2[i]);
			int xmi = min(x1[j], x2[j]);
			int xma = max(x1[j], x2[j]);
			if(ymi < y1[j] && yma > y1[j] && xmi < x1[i] && xma > x1[i]) { 
				ori[++pcnt] = {x1[i],y1[j],pcnt, 4};
			} 
		} 
	} 

	for(int i=1;i<=pcnt;++i) 
		sth[i] = ori[i];
//搞出邻居
	sort(sth+1,sth+pcnt+1,cmp1);

	for(int i=1;i<pcnt;++i) 
		if(sth[i].x == sth[i+1].x) 
			neig[sth[i].pos][0] = sth[i+1].pos;

	for(int i=2;i<=pcnt;++i) 
		if(sth[i].x == sth[i-1].x) 
			neig[sth[i].pos][2] = sth[i-1].pos;

	sort(sth+1,sth+pcnt+1,cmp2);
	
	for(int i=1;i<pcnt;++i) 
		if(sth[i].y == sth[i+1].y) 
			neig[sth[i].pos][3] = sth[i+1].pos;
			
	for(int i=2;i<pcnt;++i) 
		if(sth[i].y == sth[i-1].y)	
			neig[sth[i].pos][1] = sth[i-1].pos;
	
	for(int i=1;i<=pcnt;++i) { 
//		printf("%d :  %d %d %d %d %d %d\n",i,ori[i].x,ori[i].y,neig[i][0],neig[i][1],neig[i][2],neig[i][3]);
	} 
	//起点定向
	long long totlen = 0;
	int nloc = 1,ndir = 0;
	int mark = 0;
	if(x1[1] == x2[1]) { 
		if(y1[1] > y2[1]) 
			ndir = 0;
		else
			ndir = 2;
	} 
	else { 
		if(x1[1] > x2[1]) 
			ndir = 3;
		else
			ndir = 1;
	} 
	//算出一圈多长
	while(1) { //only contact not turn
		if(nloc == 1) { 
			if(mark) 
				break;
			else
				mark = 1;
		} 
		if(ori[nloc].type == 1) { 
			ndir = (ndir + 2) % 4;
		} 
		else if(ori[nloc].type == 4) { 
			ndir = (ndir + 1) % 4;
		} 
		int tar = neig[nloc][ndir];
		int deltalen = gdist(nloc, tar);
		totlen += deltalen, nloc = tar;
	} 
	T %= totlen;
	//redirection
	if(x1[1] == x2[1]) { 
		if(y1[1] > y2[1]) 
			ndir = 0;
		else
			ndir = 2;
	} 
	else { 
		if(x1[1] > x2[1]) 
			ndir = 3;
		else
			ndir = 1;
	} 
	while(1) { //only contact not turn
		if(ori[nloc].type == 1) { 
			ndir = (ndir + 2) % 4;
		} 
		else if(ori[nloc].type == 4) { 
			ndir = (ndir + 1) % 4;
		} 
		int tar = neig[nloc][ndir];
		int deltalen = gdist(nloc, tar);
		if(T >= deltalen) { 
			T -= deltalen;
			nloc = tar;			
		}
		else { 
			if(ori[nloc].x == ori[tar].x) { 
				if(ori[tar].y > ori[nloc].y) { 
					printf("%d %d\n",ori[nloc].x, ori[nloc].y + T);
					return 0;
				} 
				else { 
					printf("%d %d\n",ori[nloc].x, ori[nloc].y - T);
					return 0;					
				} 
			} 
			else { 
				if(ori[tar].x > ori[nloc].x) { 
					printf("%d %d\n",ori[nloc].x + T, ori[nloc].y);
					return 0;
				} 
				else { 
					printf("%d %d\n",ori[nloc].x - T, ori[nloc].y);
					return 0;
				} 
			} 
		} 
	} 
	return 0;
} 
 

总结一下吧,这次训练赛的时间有点阴间,1430-1930,中途我和ghj都出现了犯困降频的情况HHHHH。大家的状态都很不错,无论是代码稳定性还是思维速度,都发挥出了队伍应有的水准,且没有出现三人卡三题的要命情况,赛时沟通到位。赛后的萨莉亚真好吃HHHHH

体现出来的问题:
连续两场比赛的DP优化题,我们都知道这个是DP优化,但是都不会做。这波是漏知识点。本场的L题与上一场的WQS二分,都是DP优化里面的内容,感觉这波得补,不然吃枣药丸。
读题读题读题读题读题读题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值