T1 matrix
n
×
m
≤
5
×
1
0
5
,
1
≤
a
i
,
j
≤
1
0
9
n\times m\leq 5\times 10^5,1\leq a_{i,j}\leq 10^9
n×m≤5×105,1≤ai,j≤109
固定左右端点 [ l , r ] [l,r] [l,r]和字符串 S S S,设所有构成字符串为 S S S的左右端点为 [ l , r ] [l,r] [l,r]的行集合 P ∣ S ∣ , [ l , r ] = { a 1 , a 2 , . . . , a ∣ P ∣ } P_{|S|,[l,r]}=\{a_1,a_2,...,a_{|P|}\} P∣S∣,[l,r]={a1,a2,...,a∣P∣}。
统计每个子矩阵价值时,不妨从上到下遍历每行,在某行所构成的字符串第一次出现时将价值+1。
反过来对于
P
∣
S
∣
,
[
l
,
r
]
P_{|S|,[l,r]}
P∣S∣,[l,r]中的每个元素,对答案的贡献即为
(
a
i
−
a
i
−
1
)
×
(
n
−
a
i
+
1
)
(a_{i}-a_{i-1})\times(n-a_i+1)
(ai−ai−1)×(n−ai+1)(设
a
0
=
0
a_0=0
a0=0)。
暴力枚举
[
l
,
r
]
,
∣
S
∣
[l,r],|S|
[l,r],∣S∣显然不可取。考虑固定左端点。
首先设
l
=
1
l=1
l=1,建立横向的
trie
\text{trie}
trie树。
trie
\text{trie}
trie树上的每个点都包含着一个
P
P
P集合,可以用
s
e
t
set
set维护。
每次 l l l右移一位,相当于启发式合并 t r i e trie trie树上的一些结点。
复杂度 O ( n m log 2 ( n m ) ) O(nm\log^2(nm)) O(nmlog2(nm))
#include<bits/stdc++.h>
#define rv(r,c) ((r-1)*m+c)
#define ip map<int,int>::iterator
#define it set<int>::iterator
#define fi first
#define sc second
using namespace std;
const int N=5e5+10;
typedef long long ll;
int n,m,a[N],bs,cnt,son[N],cot;
ll ans,nw,vl[N];
map<int,int>ch[N];
set<int>st[N];
char cp;
inline void rd(int &x)
{
cp=getchar();x=0;
for(;!isdigit(cp);cp=getchar());
for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
}
inline void ins(int rw,int pos)
{
int i,j,u=0;it d;
for(i=1;i<=m;++i){
j=a[pos+i];
if(!ch[u][j]){ch[u][j]=++cnt;st[cnt].insert(0);}
u=ch[u][j];d=--st[u].lower_bound(rw);vl[u]+=(ll)(rw-(*d))*(n-rw+1);
st[u].insert(rw);
}
}
inline int merge(int x,int y)
{
if((!x)||(!y)) return x+y;
if(st[x].size()>st[y].size()) swap(x,y);
nw-=(vl[x]+vl[y]);int i,j;it d,e;ip c;
for(d=++st[x].begin();d!=st[x].end();++d){
e=--st[y].lower_bound(*d);i=*e;j=*d;
vl[y]+=(ll)(j-i)*(n-j+1);
if((++e)!=st[y].end()) vl[y]+=(ll)(i-j)*(n-(*e)+1);
st[y].insert(j);
}
nw+=vl[y];
for(c=ch[x].begin();c!=ch[x].end();++c) ch[y][c->fi]=merge(ch[y][c->fi],c->sc);
return y;
}
inline void sol()
{
ip c;int x;
for(c=ch[0].begin();c!=ch[0].end();++c) son[++cot]=c->sc;
ch[0].clear();
for(;cot;--cot){
x=son[cot];nw-=vl[x];
for(c=ch[x].begin();c!=ch[x].end();++c) ch[0][c->fi]=merge(ch[0][c->fi],c->sc);
}
}
int main(){
int i,j;
rd(n);rd(m);bs=n*m;
for(i=1;i<=bs;++i) rd(a[i]);
for(j=1,i=1;i<=bs;i+=m,++j) ins(j,i-1);
for(i=1;i<=cnt;++i) nw+=vl[i];ans=nw;
for(i=2;i<=m;++i) {sol();ans+=nw;}
printf("%lld",ans);
return 0;
}
T2 sequence
n
≤
1
0
5
,
q
≤
5
×
1
0
5
,
k
≤
3
,
a
i
<
2
30
n\leq 10^5,q\leq 5\times10^5,k\leq 3,a_i<2^{30}
n≤105,q≤5×105,k≤3,ai<230。
k
≤
3
k\leq 3
k≤3并没有什么用(虽然有暴力分)。
考虑每个数向右并的过程中最多变换
log
a
i
\log_{a_i}
logai次。
直接离线线段树维护即可。
时间复杂度
O
(
n
log
n
log
a
i
+
q
log
n
)
O(n\log n\log a_i+q\log n)
O(nlognlogai+qlogn)
#include<bits/stdc++.h>
#define mid (l+r>>1)
#define lc k<<1
#define rc k<<1|1
using namespace std;
const int N=1e5+10,MX=(1<<30)-1;
typedef long long ll;
int n,m,K,a[N],nxt[N][30];
int ad[N<<2];ll ss[N<<2],ans[N*5];
struct Q{int l,r,id;bool operator<(const Q&ky)const{return l>ky.l;}}q[N*5];
struct P{int w,p;bool operator<(const P&ky)const{return p<ky.p;}}ex[30];
char cp;
inline void rd(int &x)
{
cp=getchar();x=0;
for(;!isdigit(cp);cp=getchar());
for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
}
void prit(ll x){if(x>9) prit(x/10);putchar('0'+x%10);}
void ins(int k,int l,int r,int L,int R)
{
ss[k]+=(R-L+1);
if(l==L && r==R) {ad[k]++;return;}
if(L<=mid) ins(lc,l,mid,L,min(R,mid));
if(R>mid) ins(rc,mid+1,r,max(L,mid+1),R);
}
inline void ext(int pos)
{
int i,j,vl=MX,pr=pos;
for(i=0;i<30;++i) ex[i]=(P){i,nxt[pos][i]};
sort(ex,ex+30);
for(i=j=0;i<30;i=j){
if(ex[i].p>pos && vl%K==0) ins(1,1,n,pr,ex[i].p-1);
for(;j<30 && ex[j].p==ex[i].p;++j) vl^=(1<<ex[j].w);pr=ex[i].p;
}
if(pr<=n) ins(1,1,n,pr,n);
}
ll ask(int k,int l,int r,int R)
{
if(R==r) return ss[k];
ll re=ask(lc,l,mid,min(R,mid));
if(R>mid) re+=ask(rc,mid+1,r,R);
return re+(ll)ad[k]*(R-l+1);
}
int main(){
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
int i,j;
rd(n);rd(m);rd(K);
for(i=1;i<=n;++i) rd(a[i]);
for(i=0;i<30;++i) nxt[n+1][i]=n+1;
for(i=n;i;--i)
for(j=0;j<30;++j)
nxt[i][j]=((a[i]>>j)&1)?nxt[i+1][j]:i;
for(i=1;i<=m;++i) rd(q[i].l),rd(q[i].r),q[i].id=i;
sort(q+1,q+m+1);
for(i=n,j=1;i && j<=m;--i){
for(ext(i);j<=m && q[j].l==i;++j)
ans[q[j].id]=ask(1,1,n,q[j].r);
}
for(i=1;i<=m;++i) prit(ans[i]),putchar('\n');
return 0;
}
T3 permutation
CF960G
题解
只能拿 99 p t s 99pts 99pts。因为存在一个 O ( n log n ) O(n\log n) O(nlogn)倍增求解第一类斯特林数的方法(之后会学)。
小结
我真傻,真的,明明T2是个一眼题,非要去肝T1,还没有肝出来。
我真傻,真的,明明T2离线下来只需要裸的线段树,写了个主席树又
M
L
E
MLE
MLE又
W
A
WA
WA。
T3做过不说了。
不过T1匹配转 t r i e trie trie树真的挺巧妙的。