洛谷【LGR-065】洛谷11月月赛 III赛后总结


比赛链接:

Div1:https://www.luogu.org/contest/23455

Div2:https://www.luogu.org/contest/23456

Div2 T1

不就是把 0 0 0设成 1 1 1,把 1 1 1设成 − 1 -1 1,然后求最大非空字段和。

最大字段和的做法上网查吧。

非空特判就行了。

#include<cstdio>
#include<cstring>
#define  N  110000
using  namespace  std;
char  st[N];
int  f[N],n;
int  main()
{
	scanf("%s",st+1);n=strlen(st+1);
	int  sum=0,ans=0;bool  bk=0;
	for(int  i=1;i<=n;i++)f[i]=(st[i]=='0'?1:-1);
	for(int  i=1;i<=n;i++)
	{
		if(f[i]==1)bk=1;
		sum+=f[i];
		if(sum>ans)ans=sum;
		if(sum<0)sum=0;
	}
	if(bk==0)ans=-1;//特判
	printf("%d\n",ans);
	return  0;
}

Div2 T2

这道题目乍一看很难,但是仔细一看,环的异或和都是 0 0 0,那么意味着什么呢:

我们设 a a a x o r xor xor b = 0 b=0 b=0 a = a= a=~ b b b,所以 a n s ans ans x o r xor xor a a a = = =~ ( a n s (ans (ans x o r xor xor ~ a ) = a)= a)=~ ( a n s (ans (ans x o r xor xor b ) b) b)

所以我们只要固定一个根 1 1 1,用 B F S BFS BFS算出每个点的 d i s i dis_{i} disi,然后对于 l , r l,r l,r,只要取 d i s l dis_l disl x o r xor xor d i s r dis_{r} disr和他的取反值的最大值就行了。

#include<cstdio>
#include<cstring>
#define  N  110000
#define  NN  410000
using  namespace  std;
struct  node
{
	int  y,c,next;
}tr[NN];int  len,last[N];
inline  void  ins(int  x,int  y,int  c){len++;tr[len].y=y;tr[len].c=c;tr[len].next=last[x];last[x]=len;}
int  a[N],n,m,q;bool  v[N];
int  list[N],head,tail;
int  main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int  i=1;i<=m;i++)
	{
		int  x,y,c;scanf("%d%d%d",&x,&y,&c);
		ins(x,y,c);ins(y,x,c);
	}
	v[1]=1;a[1]=0;list[++tail]=1;
	while(head<=tail)
	{
		int  x=list[head++];
		for(int  k=last[x];k;k=tr[k].next)
		{
			int  y=tr[k].y;
			if(!v[y])
			{
				v[y]=1;
				list[++tail]=y;
				a[y]=a[x]^tr[k].c;
			}
		}
	}
	for(int  i=1;i<=q;i++)
	{
		int  x,y;scanf("%d%d",&x,&y);
		printf("%d\n",a[x]^a[y]);
	}
	return  0;
}

Div1 T1

这道题目当时想到了正解,但是做题太少否定了QAQ。

我们定一个数组 b b b,表示先手取到这个数字的输赢状态, 1 1 1表示必赢, 0 0 0表示必输。

首先对于 l , r l,r l,r,我们观察 r r r,如果 a [ r ] % 2 = = 1 a[r]\%2==1 a[r]%2==1,那么很明显先到 r r r的会赢,那么 b [ r ] = 1 b[r]=1 b[r]=1,反之为 0 0 0

而对于一个数字 b [ i ] = 1 b[i]=1 b[i]=1,那么很明显 b [ i − m ] b[i-m] b[im]~ b [ i − 1 ] b[i-1] b[i1]都是 0 0 0。(因为后手可以取这个,然后赢掉)。

而对于 b [ i + 1 ] b[i+1] b[i+1]~ b [ i + m ] b[i+m] b[i+m]都是 0 0 0的位置 i i i而言,如果他的数字为偶数的话,那么先手取他也肯定是先手取后面的 0 0 0,所以是 0 0 0,反之(奇数)为 1 1 1,然后重复此操作。

然后 b [ l ] b[l] b[l]就是答案。

但是这样是 O ( n 2 m ) O(\frac{n^2}{m}) O(mn2)的,很明显的爆炸。

那么我们分析一下做法。

就是对于一个数字 1 1 1,前面 m m m个数字都是 0 0 0,然后 m m m个之前的第一个奇数就是 1 1 1

对于一个位置 i i i,他的 m m m个之前的第一个奇数是固定的。

所以我们对于位置 i i i,可以向前面的那个奇数连一条边,那么就是如果 a [ l ] a[l] a[l]是偶数的话那么肯定无解,而 a [ l ] a[l] a[l]是奇数的话,就看 l l l的子树内有没有 r r r,这个我们可以记录每个数字的 D F S DFS DFS序以及子树 D F S DFS DFS区间,然后 O ( 1 ) O(1) O(1)查询。

顺便提一下,由于没有一个统一的根,所以我们设一个根,连向那些森林的跟,也就向从数列第一个奇数开始的后面 m − 1 m-1 m1减一范围内的奇数都连一条边。

口胡无代码。

时间复杂度: O ( n ) O(n) O(n)

Div1 T2

这道题目我们要明白一个惯用的套路,就是我们很难处理负数的情况,我们就要通过加 k k k操作把每个数字的活动区间变成 [ 0 , 2 k ] [0,2k] [0,2k],而 a i ′ = a i + i ∗ k a_i'=a_i+i*k ai=ai+ik。(下面的题解皆按照这个条件。)

讲真这样子化简这道题目就真的是道纯纯粹粹的SB题了。

很明显对于 a i > a j ( i < j ) a_{i}>a_{j}(i<j) ai>aj(i<j),由于我们加的数字是自然数,所以肯定如果前面到了 a j + 1 a_j+1 aj+1,后面就肯定倒不回去了。

所以我们设 a i = a j a_i=a_j ai=aj,也就是维护 a a a数组递增,每个 a i a_i ai等于后面的 a a a的最小值。

然后我们发现这道题目是很明显的贪心,我们先化化式子QMQ。

b i b_{i} bi是第 i i i次加的数字

∑ i = 1 n ∑ j = i n b i w j \sum\limits_{i=1}^{n}\sum\limits_{j=i}^{n}b_{i}w_{j} i=1nj=inbiwj

然后我们设 c i = ∑ j = i n w j c_{i}=\sum\limits_{j=i}^{n}w_{j} ci=j=inwj(其实就是把后效性消除了)。

那么式子就化成了 ∑ i = 1 n b i w i \sum\limits_{i=1}^{n}b_{i}w_{i} i=1nbiwi,那么对于大的 w w w,我们就可以给他分 2 k 2k 2k个。

但是我们又意识到了还有 a a a的限制,所以对于 w i w_{i} wi加的数字,我们需要在 [ i , n ] [i,n] [i,n]一起减去(对于第 i i i个位置初始值为 a i a_{i} ai),同时每次要给 i i i加数字的时候,我们设 [ i , n ] [i,n] [i,n]的最小值为 x x x,那么就是 m i n ( x , 2 k ) min(x,2k) min(x,2k)

支持这种操作的数据结构就是我们可爱的线段树啦!!!!

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

Div1 T3&T4

不会

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值