12.5日记
线段树
- OpenJ2528:画板长度1-10000000,依次贴了n<=1e4张海报,问最后有几张不同的画板露了出来。
思路:区间修改,最后暴力单点查询,直接把v数组当lazy用即可。还是记住,下传之后自己必须清零,否则会出问题。最后用unordered_set去重即可。不知道为什么开4*1e4会RE……开1e5就好了。
#include<bits/stdc++.h>
using namespace std;
#define mid (l+r)/2
const int M=1e5+20;
int v[M*4],l[M],r[M];
vector<int> a;
unordered_map<int,int> rev;
unordered_set<int> st;
inline void push_down(int id,int l,int r){
if (v[id])
v[id*2]=v[id*2+1]=v[id],v[id]=0;
}
void build(int id,int l,int r){
v[id]=0;
if (l==r)
return;
build(id*2,l,mid);
build(id*2+1,mid+1,r);
}
void operate(int id,int l,int r,int ql,int qr,int x){
if (ql<=l&&r<=qr){
v[id]=x;
return;
}
push_down(id,l,r);
if (ql<=mid)
operate(id*2,l,mid,ql,qr,x);
if (mid<qr)
operate(id*2+1,mid+1,r,ql,qr,x);
}
int query(int id,int l,int r,int pos){
if (l==r)
return v[id];
push_down(id,l,r);
if (pos<=mid)
return query(id*2,l,mid,pos);
else
return query(id*2+1,mid+1,r,pos);
}
int main(){
int T;
scanf("%d",&T);
for(int z=1;z<=T;++z){
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d%d",&l[i],&r[i]),a.push_back(l[i]),a.push_back(r[i]);
sort(a.begin(),a.end());
int len=unique(a.begin(),a.end())-a.begin();
for(int i=0;i<len;++i)
rev[a[i]]=i+1;
build(1,1,len);
for(int i=1;i<=n;++i)
operate(1,1,len,rev[l[i]],rev[r[i]],i);
for(int i=1;i<=len;++i)
st.insert(query(1,1,len,i));
printf("%d
",st.size());
a.clear(),st.clear();
}
return 0;
}
- HDU1540:给n个点,每个点和相邻的点有地道相连。三个操作,1,炸毁某个点和其相邻的地道。2,修好上一个被炸毁的点和地道。3,询问某个点所在连通块的大小。
思路:不知道这个题和线段树有什么关系……用set做就好了。set里存被炸毁的点。对于操作1直接insert。对于操作3,每个insert之后加入一个stack里,每次操作3就取stack栈顶元素再erase。对于操作2(假设询问x),如果x在集合里,输出0。否则找到集合里比他大和比他小的元素(就是相邻最接近的被炸毁的点),相减就是连通块大小,注意处理边界情况。然后就切了,比较好写。
#include<bits/stdc++.h>
using namespace std;
const int M=5e4+20;
set<int> st;
stack<int> sta;
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
st.clear();
while(!sta.empty())
sta.pop();
for(int i=1;i<=m;++i){
char s[2];
int x;
scanf("%s",s);
if (s[0]=='D')
scanf("%d",&x),st.insert(x),sta.push(x);
else if (s[0]=='Q'){
scanf("%d",&x);
if (st.count(x))
printf("0
");
else{
set<int>::iterator it=st.lower_bound(x),itl=it;
int lef,rt;
if (it==st.begin())
lef=1;
else
lef=*(--itl)+1;
if (it==st.end())
rt=n;
else
rt=*it-1;
printf("%d
",rt-lef+1);
}
}
else
st.erase(sta.top()),sta.pop();
}
}
return 0;
}
单调数据结构
- HDU6319:询问每个[i,i+m-1]区间内,最大值和从i到最大值的最长严格上升子序列长度。
思路:首先根据滑动窗口想到单调队列。如果从左往右,那么最大值是可以一次性求出来的,问题在于最长严格上升子序列的长度。实际上这道题应该从后往前扫,这样可以保证右端点截止在最大值,左端点一定包含第i个数。
另外这题卡常,如果用stl的deque的话可能会T。不过最后我这份代码过了,只要取模部分注意一下即可。
我吐了,少了个取模快了2s。
另外,亲测register无用。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int M=1e7+20;
int a[M];
deque<int> qu;
int main(){
int T;
scanf("%d",&T);
for(int z=1;z<=T;++z){
int n,m,k,p,q,r,mod;
LL A=0,B=0;
scanf("%d%d%d%d%d%d%d",&n,&m,&k,&p,&q,&r,&mod);
for(int i=1;i<=k;++i)
scanf("%d",&a[i]);
for(int i=k+1;i<=n;++i)
a[i]=(1LL*p*a[i-1]+1LL*q*i+r)%mod;
for(int i=n;i>=n-m+2;--i){
while(!qu.empty()&&a[i]>=a[qu.front()])
qu.pop_front();
qu.push_front(i);
}
for(int i=n-m+1;i>=1;--i){
if (qu.back()-i+1>m)
qu.pop_back();
while(!qu.empty()&&a[i]>=a[qu.front()])
qu.pop_front();
qu.push_front(i),A+=a[qu.back()]^i,B+=qu.size()^i;
}
printf("%lld %lld
",A,B),qu.clear();
}
return 0;
}