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重构树]
点击查看代码
#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【数据结构-线段树专题】
鹤的题解,网上搜去,肯定讲的比我好......