【日记】 12.5

12.5日记

线段树

  1. OpenJ2528:画板长度1-10000000,依次贴了n<=1e4张海报,问最后有几张不同的画板露了出来。

思路:区间修改,最后暴力单点查询,直接把v数组当lazy用即可。还是记住,下传之后自己必须清零,否则会出问题。最后用unordered_set去重即可。不知道为什么开4*1e4会RE……开1e5就好了。

#include
using namespace std;
#define mid (l+r)/2
const int M=1e5+20;
int v[M*4],l[M],r[M];
vector a;
unordered_map rev;
unordered_set 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
  1. HDU1540:给n个点,每个点和相邻的点有地道相连。三个操作,1,炸毁某个点和其相邻的地道。2,修好上一个被炸毁的点和地道。3,询问某个点所在连通块的大小。

思路:不知道这个题和线段树有什么关系……用set做就好了。set里存被炸毁的点。对于操作1直接insert。对于操作3,每个insert之后加入一个stack里,每次操作3就取stack栈顶元素再erase。对于操作2(假设询问x),如果x在集合里,输出0。否则找到集合里比他大和比他小的元素(就是相邻最接近的被炸毁的点),相减就是连通块大小,注意处理边界情况。然后就切了,比较好写。

#include
using namespace std;
const int M=5e4+20;
set st;
stack 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\n");
                else{
                    set::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\n",rt-lef+1);
                }
            }
            else
                st.erase(sta.top()),sta.pop();
        }
    }
    return 0;
}

单调数据结构

  1. HDU6319:询问每个[i,i+m-1]区间内,最大值和从i到最大值的最长严格上升子序列长度。

思路:首先根据滑动窗口想到单调队列。如果从左往右,那么最大值是可以一次性求出来的,问题在于最长严格上升子序列的长度。实际上这道题应该从后往前扫,这样可以保证右端点截止在最大值,左端点一定包含第i个数。

另外这题卡常,如果用stl的deque的话可能会T。不过最后我这份代码过了,只要取模部分注意一下即可。

我吐了,少了个取模快了2s。

另外,亲测register无用。

#include
using namespace std;
#define LL long long
const int M=1e7+20;
int a[M];
deque 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\n",A,B),qu.clear();
    }
    return 0;
}

你可能感兴趣的:(【日记】 12.5)