2019雅礼集训day5 题解

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×m5×105,1ai,j109

固定左右端点 [ 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|}\} PS,[l,r]={a1,a2,...,aP}

统计每个子矩阵价值时,不妨从上到下遍历每行,在某行所构成的字符串第一次出现时将价值+1。
反过来对于 P ∣ S ∣ , [ l , r ] P_{|S|,[l,r]} PS,[l,r]中的每个元素,对答案的贡献即为 ( a i − a i − 1 ) × ( n − a i + 1 ) (a_{i}-a_{i-1})\times(n-a_i+1) (aiai1)×(nai+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 &lt; 2 30 n\leq 10^5,q\leq 5\times10^5,k\leq 3,a_i&lt;2^{30} n105,q5×105,k3,ai<230

k ≤ 3 k\leq 3 k3并没有什么用(虽然有暴力分)。
考虑每个数向右并的过程中最多变换 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树真的挺巧妙的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值