20231019刷题记录

算法模板:拓扑排序、线段树与滑动窗口在C++中的实现
  • P3387 【模板】缩点

    sol.

    注意建边、拓扑序 DP 过程,弹栈的时候最后要把当前搜到的 x x x 也弹出。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn=1e4+5,maxm=1e5+5;
    int head1[maxn],head2[maxn],a[maxn],low[maxn],scc[maxn]/*sizeof scc*/,dfscnt,dfn[maxn],f[maxn],s[maxn],top,scccnt,cnt1,cnt2,sccnum[maxn];
    struct edge{int to,nxt;}e1[maxm],e2[maxm];
    bool vis[maxn];
    
    void add1(int x,int y){e1[++cnt1]=(edge){y,head1[x]},head1[x]=cnt1;}
    
    void add2(int x,int y){e2[++cnt2]=(edge){y,head2[x]},head2[x]=cnt2;}
    
    void tarjan(int x)
    {
        low[x]=dfn[x]=++dfscnt,s[++top]=x,vis[x]=1;
        for(int i=head1[x];i;i=e1[i].nxt)
        {
            if(!dfn[e1[i].to]) tarjan(e1[i].to),low[x]=min(low[x],low[e1[i].to]);
            else if(vis[e1[i].to]) low[x]=min(low[x],dfn[e1[i].to]);
        }
        if(dfn[x]==low[x])
        {
            sccnum[x]=++scccnt;
            while(s[top]!=x) sccnum[s[top]]=scccnt,vis[s[top--]]=0;
            vis[x]=0,top--;
        }
    }
    
    int main()
    {
        int n,m;cin>>n>>m;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1,u,v;i<=m;i++) cin>>u>>v,add1(u,v);
        for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
        for(int i=1;i<=n;i++)
        {
            scc[sccnum[i]]+=a[i];
            for(int j=head1[i];j;j=e1[j].nxt) if(sccnum[i]!=sccnum[e1[j].to]) add2(sccnum[i],sccnum[e1[j].to]);
        }
        int ans=-1;
        for(int ii=scccnt;ii;ii--)
        {
            f[ii]+=scc[ii],ans=max(ans,f[ii]);
            for(int i=head2[ii];i;i=e2[i].nxt) f[e2[i].to]=max(f[e2[i].to],f[ii]);
        }
        cout<<ans;
        return 0;
    }
    
  • P3373 【模板】线段树 2

    注意乘法的 lazy 标记要初始化为 1 1 1

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    const int maxn=4e5+5;
    int a[maxn],s[maxn],la1[maxn],la2[maxn],m,k;//la1-multiple,la2-plus
    
    void build(int k, int l,int r)
    {
        la1[k]=1;
        if(l==r){s[k]=a[l]%m;return;}
        int mid=(l+r)/2;
        build(k*2,l,mid),build(k*2+1,mid+1,r),s[k]=(s[k*2]+s[k*2+1])%m;
    }
    
    void add(int k,int l,int r,int q1,int q2)//q1-multiple,q2-plus
    {
        s[k]=(s[k]*q1%m+(r-l+1)*q2%m)%m;
        la2[k]=(la2[k]*q1+q2)%m,la1[k]=(la1[k]*q1)%m;
    }
    
    void pushup(int k){s[k]=(s[k*2]+s[k*2+1])%m;}
    
    void pushdown(int k,int l,int r)
    {
        int mid=(l+r)/2;
        add(k*2,l,mid,la1[k],la2[k]),add(k*2+1,mid+1,r,la1[k],la2[k]);
        la1[k]=1,la2[k]=0;
    }
    
    void update(int k,int l,int r,int x,int y,int laz1,int laz2)
    {
        if(x<=l&&y>=r) return add(k,l,r,laz1,laz2);
        pushdown(k,l,r);
        int mid=(l+r)/2;
        if(x<=mid) update(k*2,l,mid,x,y,laz1,laz2);
        if(y>mid) update(k*2+1,mid+1,r,x,y,laz1,laz2);
        pushup(k);
    }
    
    int query(int k,int l,int r,int x,int y)
    {
        if(x<=l&&y>=r) return s[k];
        int res=0,mid=(l+r)/2;
        pushdown(k,l,r);
        if(x<=mid) res=(res+query(k*2,l,mid,x,y))%m;
        if(y>mid) res=(res+query(k*2+1,mid+1,r,x,y))%m;
        return res;
    }
    
    signed main()
    {
        int n,q;cin>>n>>q>>m;
        for(int i=1;i<=n;i++) cin>>a[i];
        build(1,1,n);
        for(int i=1,op,x,y;i<=q;i++)
        {
            cin>>op>>x>>y;
            if(op==1) cin>>k,update(1,1,n,x,y,k,0);
            else if(op==2) cin>>k,update(1,1,n,x,y,1,k);
            else cout<<query(1,1,n,x,y)<<endl;
        }
        return 0;
    }
    
  • P1886 滑动窗口 /【模板】单调队列

    维护单调上升/下降的序列。对于一个新加入的元素,先弹出超出滑动窗口长 k k k 的队首元素,然后考虑向队尾加入元素,关键是要维护序列的单调性。双端队列存储的是序列下标。

    注意数组大小。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn=1e6+5;
    int q1[maxn],q2[maxn],a[maxn];
    
    int main()
    {
        int n,k;cin>>n>>k;
        for(int i=1;i<=n;i++) cin>>a[i];
        int h1=1,t1=0,h2=1,t2=0;
        for(int i=1;i<=n;i++)
        {
            while(h1<=t1&&q1[h1]+k<=i) h1++;
            while(h1<=t1&&a[q1[t1]]>a[i]) t1--;
            q1[++t1]=i;
            if(i>=k) cout<<a[q1[h1]]<<' ';
        }
        cout<<endl;
        for(int i=1;i<=n;i++)
        {
            while(h2<=t2&&q2[h2]+k<=i) h2++;
            while(h2<=t2&&a[q2[t2]]<a[i]) t2--;
            q2[++t2]=i;
            if(i>=k) cout<<a[q2[h2]]<<' ';
        }
        return 0;
    }
    
  • P8306 【模板】字典树

    Portal.

    注意清空根据上一步的 tot,不要每次都 memset

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值