粗节?不,是细节

什么叫细节?是O与o之间的区别吗?还是S与s之间的区别吗?
nonono…


昨天,我入坑了一道题…:
1102: [视频]线段树4(统计线段数目)
【题意】
有n(1~100000)个连续的格子,编号为1……n,每个格子的颜色有3种(分别是1、2、3)。
有m(1~100000)操作,操作有2种:
1 x y k:表示第x个格子至第y个格子全染色为k(1<=k<=3)
2 x y:表示询问第x个格子至第y个格子有多少条线段(相邻两个格子的颜色相同则同属一条线段)。
【输入格式】
第一行n和m。
第二行n个数,分别表格n个格子的颜色。
下来m行,每行表示一个操作。
【输出格式】
遇到操作2,则输出答案
【样例输入】
5 5
2 1 1 2 1
2 1 5
1 4 4 1
2 1 5
1 1 1 1
2 1 5
【样例输出】
4
2
1

提交记录…

17200	2020zsyz20	1102 正确
17198	2020zsyz20	1102 答案错误80%
17197	2020zsyz20	1102 答案错误80%
17196	2020zsyz20	1102 运行错误
17195	2020zsyz20	1102 时间超限80%
17194	2020zsyz20	1102 时间超限
17190	2020zsyz20	1102 运行错误

可能这也不多吧。但是,看看时间吧:

2020-08-27 21:37:25
2020-08-27 20:56:48
2020-08-27 20:49:27
2020-08-27 20:49:06
2020-08-27 20:47:12
2020-08-27 20:45:21
2020-08-27 17:57:44

这道题,思路是非常简单的…线段树,每一个struct内的data表示此线段中的线段的个数。但是,最难的就是,此题目的代码复杂度是比较高的。就算是第一次得了20分的代码:(无注释)

#include
#include
using namespace std;
int num,root;
struct node{
     
	int l,r;
	int lc,rc;
	int ys;
	int ly,ry;
	int data;
	int lazy;
}tree[200001];
int a[100001];/*
int sum=0;
char read;

int inin()
{
	sum=0;
	read=char(getchar());
	while(read<48||read>57) read=char(getchar());
	while(read!=' '&&read!='\n') sum+=sum*10+read-48,read=char(getchar());
	return sum;
}*/

void solazy(int now)
{
     
	if(tree[now].lazy)
	{
     
		tree[now].lazy=false;
		tree[tree[now].lc].data=tree[now].data;
		tree[tree[now].lc].lazy= true;
		tree[tree[now].rc].data=tree[now].data;
		tree[tree[now].rc].lazy= true;
	}
}

int sum=0;

int bt(int l,int r)
{
     
	int p=++num;
	tree[p].l=l;
	tree[p].r=r;
	tree[p].lc=tree[p].rc=-1;
	if(l==r) tree[p].data=1,tree[p].ys=a[++sum],tree[p].ly=tree[p].ry=a[sum],tree[p].lazy=false;
	else
	{
     
		int mid=(l+r)/2;
		tree[p].lc=bt(l,mid);
		tree[p].rc=bt(mid+1,r);
		if(tree[tree[p].lc].ys!=tree[tree[p].rc].ys) tree[p].ys=-1;
		else tree[p].ys=tree[tree[p].lc].ys;
		tree[p].ly=tree[tree[p].lc].ly;tree[p].ry=tree[tree[p].rc].ry;
		tree[p].data=tree[tree[p].lc].data+tree[tree[p].rc].data;
		if(tree[tree[p].lc].ry==tree[tree[p].rc].ly) tree[p].data--;
	}
	return p;
}

void change(int now,int x,int y,int data)
{
     
	if(tree[now].r==y&&x==tree[now].l) tree[now].ys=data,tree[now].data=1,tree[now].ly=data,tree[now].ry=data,tree[now].lazy=true;
	else
	{
     
		solazy(now);
		int mid=(tree[now].l+tree[now].r)/2;
		if(y<=mid) change(tree[now].lc,x,y,data);
		else if(x>mid) change(tree[now].rc,x,y,data);
		else change(tree[now].lc,x,mid,data),change(tree[now].rc,mid+1,y,data);
		tree[now].data=tree[tree[now].lc].data+tree[tree[now].rc].data;
		tree[now].ly=tree[tree[now].lc].ly;tree[now].ry=tree[tree[now].rc].ry;
		if(tree[tree[now].lc].ry==tree[tree[now].rc].ly) tree[now].data--;
	}
}

int find(int now,int x,int y)
{
     
	if(tree[now].r==y&&x==tree[now].l) return tree[now].data;
	solazy(now);
	int mid=(tree[now].r+tree[now].l)/2;
	if(y<=mid)
	{
     
		return find(tree[now].lc,x,y);
	}
	if(x>mid)
	{
     
		return find(tree[now].rc,x,y);
	}
	if(tree[tree[now].lc].ry==tree[tree[now].rc].ly)
		return find(tree[now].lc,x,mid)+find(tree[now].rc,mid+1,y)-1;
	else return find(tree[now].lc,x,mid)+find(tree[now].rc,mid+1,y);
}

int main()
{
     
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	root=bt(1,n);
	while(m--)
	{
     
		int x,y,z;
		int ch;
		cin>>ch>>x>>y;
		if(x>y) swap(x,y);
		if(ch==1)
		{
     
			cin>>z,change(root,x,y,z);
		}
		if(ch==2)
		{
     
			cout<<find(root,x,y)<<endl;
		}
	}
	return 0;
}

可惜,这个代码只是得了20分。
当时我就很迷茫。不知道哪里错了?那么长的代码错了我去哪里找问题啊?整个baidu上一点都没有这道题的痕迹,抄也抄不得。想了老久老久了,我出了一组数据:
6 2
1 1 2 2 3 3
1 1 4 2
2 1 6
很简单的数据。
意思呢,初始为1 1 2 2 3 3,
将第一个数字到第四个数字都修改为2。
之后检测1~6一共有多少个线段。

结果,我看到了什么?
bt这个函数初始化树之后就错了。这时,我就开始一步步调试bt函数。
bt修改前:


int bt(int l,int r)
{
     
	int p=++num;
	tree[p].l=l;
	tree[p].r=r;
	tree[p].lc=tree[p].rc=-1;
	if(l==r) tree[p].data=1,tree[p].ys=a[p],tree[p].ly=tree[p].ry=a[p];
	else
	{
     
		int mid=(l+r)/2;
		tree[p].lc=bt(l,mid);
		tree[p].rc=bt(mid+1,r);
		if(tree[tree[p].lc].ys!=tree[tree[p].rc].ys) tree[p].ys=-1;
		else tree[p].ys=tree[tree[p].lc].ys;
		tree[p].ly=tree[tree[p].lc].ly;tree[p].ry=tree[tree[p].rc].ry;
		tree[p].data=tree[tree[p].lc].data+tree[tree[p].rc].data;
	}
	return p;
}

修改后:

int bt(int l,int r)
{
     
    int p=++num;
    tree[p].l=l;
    tree[p].r=r;
    tree[p].lc=tree[p].rc=-1;
    if(l==r) tree[p].data=1,tree[p].ys=a[l],tree[p].ly=tree[p].ry=a[l],tree[p].lazy=false;
    else
    {
     
        int mid=(l+r)/2;
        tree[p].lc=bt(l,mid);
        tree[p].rc=bt(mid+1,r);
        if(tree[tree[p].lc].ys!=tree[tree[p].rc].ys) tree[p].ys=-1;
        else tree[p].ys=tree[tree[p].lc].ys;
        tree[p].ly=tree[tree[p].lc].ly;tree[p].ry=tree[tree[p].rc].ry;
        tree[p].data=tree[tree[p].lc].data+tree[tree[p].rc].data;
        if(tree[tree[p].lc].ry==tree[tree[p].rc].ly) tree[p].data--;
    }
    return p;
}

什么?哪里有区别,不是没有区别的吗?
不,还是有区别的。
错误的代码中,if(l==r) tree[p].ys=a[p];
注意!p只是树中的代码,并不代表每一个线段的序号。所以改成:
if(l==r) tree[p].ys=a[++sum];
sum代表第几个叶子节点。
此时我们发现,l==r就代表了是第几个叶子节点,就又改成了这样:
if(l==r) tree[p].ys=a[l]if(l==r) tree[p].ys=a[r]
这,就是细节。由原始,到最后的正确,其实,只改动了约200个字符,但是,区别却非常明显。
这道题请各大奆佬思考下,本文只讲一个道理:

细节决定成败!


p.s:今天,我一个同学交了代码之后,全是内存超限:

                空间(kb)   时间(ms)
1102 内存超限	139672      112

一看数据,发现,x有可能比y大。
修改一下,结果:

1102 正确        8732       215

你可能感兴趣的:(日常,c++)