CSP-S模拟12开挂 叁仟柒佰万 超级加倍 欢乐豆

T2【DP+指针优化】给你一个序列,求序列的连续划分方案数,使得任意划分出来的子串Mex相同,Mex是序列中没出现过的最小自然数。(n<=1e7)

Mex一定就是整个序列的Mex,否则假设存在一种划分[a,b][b+1,c],mex!=Mex,mex_a=mex_b,无论mex>Mex或者<Mex都没办法满足在合并之后改变mex。
\(dp[l][r]:代表[l,r]区间是否合法\),就像括号序列一样\(O(n^3)\)DP
考虑优化,发现从最左端开始枚举\(dp[x]:代表[1,x]区间合法序列数量\)就足够了。
\(dp[x]=sigma(dp[y]) y<x&&is_legal[y+1,x]\)\(O(n^2)\)
用指针R维护到目前循环到的i位置最小合法区间,每次拓展一个数,把R回缩,同时记录合法被收缩的位置sum(以后一定也合法),在转移时候直接让dp[i]+=sum+dp[last~now]。
可以保证不重复,因为最后一个区间加上了一个新数,前面的拼接不会重复。

点击查看代码


#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
    ll x=0,h=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=37000000+100;const ll mod=1e9+7;
int T,n,a[N],cnt[N];
int dp[N];
int st[N],top;
inline void upd(int&u,int v)
{
    u=((ll)u+(ll)v)%mod;
   // if(u>=mod)u%=mod;
}
inline void Deal()
{
    _f(i,1,n)
    {
        if(a[i]>n-1)a[i]=n;
        cnt[a[i]]++;
    }
    int Mex=0;
    while(cnt[Mex])++Mex;
    dp[0]=1;int mex=0,start=0;
    _f(i,0,n)cnt[i]=0;
    _f(i,1,n)
    {
        cnt[a[i]]++;
        while(cnt[mex])++mex;
        if(mex==Mex){start=i;break;}
    }
    int last=1;//合法最小区间的左端点
    int sum=0;
  //  chu("start:%d\n",start);
    dp[start]=1;
    _f(i,start+1,n)
    {
       // chu("checl:%d  last:%d\n",i,last);
        cnt[a[i]]++;
        int meX=Mex;
        int lmx=last;//尝试向右边收缩
        while(lmx<=i&&meX==Mex)
        {
            --cnt[a[lmx]];
            if(!cnt[a[lmx]]&&a[lmx]<meX)meX=a[lmx];
            lmx++;
        }
     //   chu("最右边:%d\n",lmx-1);
        cnt[a[lmx-1]]++;
        upd(dp[i],sum);
        _f(j,last,lmx-1)
        {
            upd(dp[i],dp[j-1]);
            if(j!=lmx-1)upd(sum,dp[j-1]);
        }
       // chu("update fron:%d--%d\n",last-1,lmx-2);
        last=lmx-1;//合法的
      //  chu("sum:%d\n",sum);
      //  chu("dp[%d]:%d\n",i,dp[i]);
    }
    chu("%d\n",dp[n]);
    _f(i,0,n)dp[i]=cnt[i]=0;
}
inline void Special()
{
    int x=re(),y=re();
    a[1]=0;
    _f(i,2,n)
    {
        a[i]=(a[i-1]*x+y+i)&262143;
    }
}
inline void Normal()
{
    _f(i,1,n)a[i]=re();
}
int main()
{
  // freopen("1.in","r",stdin);
 //  freopen("1.out","w",stdout);
  T=re();
  while(T--)
  {
      n=re();
      if(n==37000000)
      {
          Special();
      }
      else Normal();
      Deal();
  }
    return 0;
}
/*
如果n<=10,直接枚举二进制数区间划分种类-->检查Mex是不是一样
如果A:dp一下

2
6
0 1 1 0 1 0
6
1 1 4 5 1 4

5
32

1
6
0 1 1 0 1 0

1
4
1 1 1 1
*/

T3[笛卡尔树+kruscal重构树]image

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
    ll x=0,h=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch<='9'&&ch>='0')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=2e6+100;
struct Node
{
    int to,nxt;
}e[N<<1];
int tot,head[N];
int n,in[N],out[N],tim,low[N];
ll sum;
struct TREE
{
    int nxt[N],to[N],fai[N],hd[N],tt;
    inline int getfa(int x)
    {
        if(x==fai[x])return x;
        return fai[x]=getfa(fai[x]);
    }
    inline void add_edge(int x,int y)
    {
        to[++tt]=y;nxt[tt]=hd[x];hd[x]=tt;
    }
    inline void rebuild_max()
    {
        _f(i,1,n)fai[i]=i;
        _f(i,1,n)
        {
            int fx=getfa(i);
            for(rint j=head[i];j;j=e[j].nxt)
            {
                int to=e[j].to;
                if(to>i)continue;
                int fy=getfa(to);//向小的连边
                if(fx==fy)continue;
                fai[fy]=fx;
                add_edge(fx,fy);
             //   chu("min_top:%d-->%d\n",fx,fy);
            }
        }
    }
    inline void rebuild_min()
    {
        _f(i,1,n)fai[i]=i;
        f_(i,n,1)
        {
            int fx=getfa(i);
            //chu("check:%d  %d\n",i,fx);
            for(rint j=head[i];j;j=e[j].nxt)
            {
                int to=e[j].to;
            //    chu("lian :%d\n",to);
                if(to<i)continue;
                int fy=getfa(to);
                if(fy==fx)continue;
                fai[fy]=fx;
                add_edge(fx,fy);
                //chu("max_top:%d-->%d\n",fx,fy);
            }
        }
    }
}Max,Min;
inline void Add(int x,int y)
{
    e[++tot].to=y;e[tot].nxt=head[x];head[x]=tot;
}
#define lowbit(x)  ((x)&(-x))
inline void insert(int x,int op)
{
    while(x<=n)
    {
        low[x]+=op;
        x+=lowbit(x);
    }
}
inline int query(int x)
{
    int nas=0;
    while(x)
    {
        nas+=low[x];
        x-=lowbit(x);
    }
    return nas;
}
inline void dfs1(int x)
{
    in[x]=++tim;
    for(rint i=Max.hd[x];i;i=Max.nxt[i])
    {
        int to=Max.to[i];
        dfs1(to);
    }
    out[x]=tim;
}
inline void dfs2(int x)
{
    sum+=query(out[x])-query(in[x]-1);//Min里面在上面,Max里面在下面的
  //  chu("%d dev:%d\n",x,query(out[x])-query(in[x]-1));
    insert(in[x],1);
    for(rint i=Min.hd[x];i;i=Min.nxt[i])
    {
        int to=Min.to[i];
        dfs2(to);
    }
    insert(in[x],-1);
}
int main()
{
   //freopen("sample_charity3.in","r",stdin);
   //freopen("1.out","w",stdout);
    n=re();
    _f(i,1,n)
    {
        int pi=re();
        if(i!=1)Add(i,pi),Add(pi,i);
    }
    Max.rebuild_max();
    Min.rebuild_min();
    dfs1(n);
    dfs2(1);
    chu("%lld",sum);
    return 0;
}
/*
7
0 3 1 2 2 7 2
*/

T4:[Dij]is the theme【数据结构-线段树专题】

鹤的题解,网上搜去,肯定讲的比我好......

posted on 2022-09-26 20:40  HZOI-曹蓉  阅读(68)  评论(0编辑  收藏  举报