在网上看到很多人写线段树的专题,我觉得线段树如此流行不仅仅是因为ACM题目里有它,而且它的实际应用价值也很高。
这道题的大意是你可以将钩子的某一段区间变为等级1(铜钩),等级2(银钩),或等级3(金钩)的钩子,问你在若干次变换后,整个钩子的值是多少。
我的想法是建一颗线段树,树的每一个节点保存有左右端点的值,这一段长度的等级(初值为0),没插入一条线段,就在插入的过程中判断经过的线段有没有被覆盖过(这就是为什么初值为0的原因),如果有被覆盖过,就把这条线段打破(就是将这条线段重新置为0,表示为没覆盖),然后再将待插入的线段继续往下更新,将打破的这条线段的余下部分更新下去(个人觉得这思路很挫。。。但是我只能想到这么做了。。。)。
比如说开始我把(1,10)的区间变为等级3,现在我要将(4,6)的区间变为等级2,那么我就先正常插入(4,6)这个线段,插入过程中先碰到(1,10)这个线段,发现这个线段已经被覆盖过了(不为0),就把(1,10)置为0,然后把(1,10)拆成3段(1,3)值为3,(4,6)值为2,(7,10)值为3,分别从当前位置插入这三条线段,直到插入的线段和遇到的线段表示的长度一致的时候就停下,并更新这一条线段。
#include<stdio.h>
struct SeqTree
{
int l,r,v;
}a[400005];
int n;
void build(int l,int r,int pos)
{
a[pos].l = l;
a[pos].r = r;
a[pos].v = 0;
if(l == r)
return ;
int mid = (l + r) >> 1;
build(l,mid,(pos << 1) + 1);
build(mid + 1,r,(pos << 1) + 2);
}
void insert(int l,int r,int v,int pos)
{
if(a[pos].l == l && a[pos].r == r)
{
a[pos].v = v;
return ;
}
int mid = (a[pos].l + a[pos].r) >> 1;
if(l > mid)
insert(l,r,v,(pos << 1) + 2);
else if(r <= mid)
insert(l,r,v,(pos << 1) + 1);
else
{
insert(l,mid,v,(pos << 1) + 1);
insert(mid + 1,r,v,(pos << 1) + 2);
}
if(a[pos].v)
{
if(r < a[pos].r)
{
if(r >= mid)
insert(r + 1,a[pos].r,a[pos].v,(pos << 1) + 2);
else
{
insert(r + 1,mid,a[pos].v,(pos << 1) + 1);
insert(mid + 1,a[pos].r,a[pos].v,(pos << 1) + 2);
}
}
if(l > a[pos].l)
{
if(l <= mid + 1)
insert(a[pos].l,l-1,a[pos].v,(pos << 1) + 1);
else
{
insert(a[pos].l,mid,a[pos].v,(pos << 1) + 1);
insert(mid + 1,l-1,a[pos].v,(pos << 1) + 2);
}
}
}
a[pos].v = 0;
}
int find(int p,int pos)
{
if(a[pos].v)
return a[pos].v;
if(a[pos].r == a[pos].l)
return a[pos].v?a[pos].v:1;
int mid = (a[pos].l + a[pos].r) >> 1;
if(p <= mid)
return find(p,(pos << 1) + 1);
else
return find(p,(pos << 1) + 2);
}
int main()
{
int T,l,r,q,v;
scanf("%d",&T);
for(int icase = 1;icase <= T;++icase)
{
scanf("%d%d",&n,&q);
build(1,n,0);
while(q--)
{
scanf("%d%d%d",&l,&r,&v);
insert(l,r,v,0);
}
int ans = 0;
for(int i = 1;i <= n;++i)
ans += find(i,0);
printf("Case %d: The total value of the hook is %d.\n",icase,ans);
}
return 0;
}