【TJOI 2009】開關
阿新 • • 發佈:2018-11-09
【題目】
題目描述:
現有 ( ≤ ≤ )盞燈排成一排,從左到右依次編號為: , ,…, 。然後依次執行 ( ≤ ≤ )項操作,操作分為兩種:第一種操作指定一個區間 [ ] ,然後改變編號在這個區間內的燈的狀態(把開著的燈關上,關著的燈開啟),第二種操作是指定一個區間 [ ] ,要求你輸出這個區間內有多少盞燈是開啟的。燈在初始時都是關著的。
輸入格式:
第一行有兩個整數
和
,分別表示燈的數目和操作的數目。
接下來有
行,每行有三個整數,依次為:
。其中
表示操作的種類,當
的值為
時,表示是第一種操作。當
的值為
時表示是第二種操作。
和
則分別表示了操作區間的左右邊界(
≤
≤
≤
)。
輸出格式:
每當遇到第二種操作時,輸出一行,包含一個整數:此時在查詢的區間中開啟的燈的數目。
樣例資料:
輸入
4 5
0 1 2
0 2 4
1 2 3
0 2 4
1 1 4
輸出
1
2
【分析】
題解:線段樹
假設 表示關著的燈, 表示開著的燈
我們用兩個陣列 表示 中 的個數, 表示 中 的個數
然後只用累計取反操作的次數(累加或異或都可以),若要取反,則交換 和 即可
剩下的就是基礎的線段樹操作了
【程式碼】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
using namespace std;
int sum[N<<2][2],mark[N<<2];
void build(int root,int l,int r)
{
if(l==r)
{
sum[root][0]=1;
return;
}
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
sum[root][0]=sum[root<<1][0]+sum[root<<1|1][0];
}
void pushdown(int root)
{
swap(sum[root<<1][0],sum[root<<1][1]);
swap(sum[root<<1|1][0],sum[root<<1|1][1]);
mark[root<<1]^=1,mark[root<<1|1]^=1,mark[root]=0;
}
void modify(int root,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
{
mark[root]^=1;
swap(sum[root][0],sum[root][1]);
return;
}
int mid=(l+r)>>1;
if(mark[root]) pushdown(root);
if(x<=mid) modify(root<<1,l,mid,x,y);
if(y>mid) modify(root<<1|1,mid+1,r,x,y);
sum[root][0]=sum[root<<1][0]+sum[root<<1|1][0];
sum[root][1]=sum[root<<1][1]+sum[root<<1|1][1];
}
int query(int root,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
return sum[root][1];
int ans=0,mid=(l+r)>>1;
if(mark[root]) pushdown(root);
if(x<=mid) ans+=query(root<<1,l,mid,x,y);
if(y>mid) ans+=query(root<<1|1,mid+1,r,x,y);
return ans;
}
int main()
{
int n,m,i,s,x,y;
scanf("%d%d",&n,&m);
build(1,1,n);
for(i=1;i<=m;++i)
{
scanf("%d%d%d",&s,&x,&y);
if(s==0) modify(1,1,n,x,y);
if(s==1) printf("%d\n",query(1,1,n,x,y));
}
return 0;
}