什么叫细节?是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