CF915E Physical Education Lessons
题目链接:http://codeforces.com/contest/915/problem/E
题目大意:
有 \(n\) 天(一开始都是工作日),之后会发布 \(q\) 个通知,通知的格式为:\(l\) \(r\) \(k\)。如果 \(k=1\),就说明从第 \(l\) 天到第 \(r\) 天都变成非工作日;如果 \(k=2\),就说明从第 \(l\) 天到第 \(r\) 天都变成工作日。问最后有多少个工作日。
知识点: 线段树、离散化
解题思路:
将(区间左端点)与(区间右端点加一)进行离散化,如此线段树维护的叶子是相邻的两个离散点之间的左闭右开区间,在此基础上进行增删操作即可。线段树似乎不是这道题的最优解,时间卡的非常极限,需要加一点剪枝才能AC,可以参考一下注释。
AC代码:
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 #define lson l,m,rt<<1 5 #define rson m+1,r,rt<<1|1 6 const int maxn = 6e5+30; 7 8 struct Input{ 9 int l,r,k; 10 }inp[maxn>>1]; 11 int numbers[maxn],len[maxn]; 12 map<int,int> index; 13 int sum[maxn<<2],lens[maxn<<2];//lens记录该区间的“总”长度,sum记录该区间的工作日数 14 int lazy[maxn<<2]; //lazy—— -1,void;1,增;0,删。 15 16 void pushdown(int l,int r,int rt){ 17 lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt]; 18 int m=(l+r)>>1; 19 sum[rt<<1]=lens[rt<<1]*lazy[rt],sum[rt<<1|1]=lens[rt<<1|1]*lazy[rt]; 20 lazy[rt]=-1; 21 } 22 void pushup(int rt){ 23 sum[rt]=sum[rt<<1]+sum[rt<<1|1]; 24 } 25 void build(int l,int r,int rt){ 26 lazy[rt]=-1; 27 if(l==r){ 28 sum[rt]=lens[rt]=len[l]; 29 return; 30 } 31 int m=(l+r)>>1; 32 build(lson); build(rson); 33 sum[rt]=lens[rt]=lens[rt<<1]+lens[rt<<1|1]; 34 } 35 void update(int L,int R,int c,int l,int r,int rt){ 36 if((sum[rt]==lens[rt]&&c)||(sum[rt]==0&&!c)) return; //在此处剪枝:如果这个区间已经满了(sum[rt]==lens[rt])此时再增加(c==1)就没有意义了,所以return;另一个同理。 37 if(L<=l&&r<=R){ 38 sum[rt]=lens[rt]*c; 39 lazy[rt]=c; 40 return; 41 } 42 if(lazy[rt]!=-1&&l<r) pushdown(l,r,rt); 43 int m=(l+r)>>1; 44 if(L<=m) update(L,min(R,m),c,lson); 45 if(R>m) update(max(L,m),R,c,rson); 46 pushup(rt); 47 } 48 int main(){ 49 int n,q,t=0; 50 scanf("%d%d",&n,&q); 51 for(int i=0;i<q;i++){ 52 scanf("%d%d%d",&inp[i].l,&inp[i].r,&inp[i].k); 53 inp[i].k--,inp[i].r++; //区间右端点加一,k减一(如此0即为删,1即为增) 54 numbers[t++]=inp[i].l,numbers[t++]=inp[i].r; 55 } 56 numbers[t++]=1,numbers[t++]=n+1; 57 sort(numbers,numbers+t); 58 int m=unique(numbers,numbers+t)-numbers; 59 for(int i=0;i<m;i++){ 60 index[numbers[i]]=i; 61 if(i<m-1) len[i]=numbers[i+1]-numbers[i]; 62 } 63 build(0,m-2,1); 64 for(int i=0;i<q;i++){ 65 update(index[inp[i].l],index[inp[i].r]-1,inp[i].k,0,m-2,1); //这部分比较费解:我们离散化的是点,但是线段树维护的是区间。 66 printf("%d\n",sum[1]); 67 } 68 return 0; 69 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”