雅礼集训2019 Day5

雅礼集训2019 Day5

matrix

对于同一连续子矩形中的行,我们只计算相同的行中最上面的,那么显然可以分开计算答案。

从上到下枚举行,再枚举起点,再从左到右枚举这一行中终点所在列,用trie树查询可以做到\(O(nm^2)\)。事实上直接hash也可以。

然后考虑n^2m做法,考虑容斥,总数-不同子矩形中相等的行数(而且要保证这两行之间没有已经算过的,)

枚举开头行和另外一行,计算另外的行在以开头行为行首的子矩形中的贡献,显然可以O(m)求出以每个点为右端点的极长相等串,往上枚举的时候取max就不会重。往上一边枚举一边计算答案,为没有被完全包含的列区间数*往下的行数

那么我们就有了\(O(nm\sqrt {nm})\)的做法,可以过70分:

#include<bits/stdc++.h>
#pragma GCC diagnostic error "-std=c++11"
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i) 
#define ll long long
#define ull unsigned long long
using namespace std;
int read(){
	int x=0,pos=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
	return pos?x:-x;
}
const int N = 5e5+20;
int n,m;
vector<int>mp[N];
namespace sub1{
	vector<ull>pr[N];
	ull bas=31;
	struct node{
		int nex,pos;ull w;
	};
	struct hash_table{
		int head[20000],top,nh[20000],tp;
		node edge[20000];
	//	ull an[N];
		void pre(){
			FOR(i,0,n-1){
				ull res=0,ba=1;
				for(int j=0;j<=m-1;j++){
					res=res+1ull*mp[i][j]*ba;ba=ba*bas;
					pr[i].push_back(res);	
				}
			}
		}
		ull Hash(int i,int l,int r){
			ull res=pr[i][r];if(l>0) res-=pr[i][l-1];//res/=
			return res;
		}
		void add(int u,ull w,int pos){
			edge[++top].w=w;
			edge[top].pos=pos;
			edge[top].nex=head[u];
			head[u]=top;
		}	
		int work(int id,int l,int r){
			ull x=Hash(id,l,r);
			int now=x%20000,rp=-1;
			for(int i=head[now];i;i=edge[i].nex){
				if(edge[i].w==x){
					rp=edge[i].pos;
					edge[i].pos=id;
					break;
				}
			}
			if(rp==-1){
				if(!head[now]) nh[++tp]=now;
				add(now,x,id);
			}
			return rp;
		}
		void clear(){
			FOR(i,1,tp) nh[i]=head[nh[i]]=0;
			FOR(i,1,top) edge[i].pos=edge[i].w=edge[i].nex=0;
			tp=top=0;
		}
	}ht;
	int b[N],tn=0;
	void solve(){
		/*FOR(i,0,n-1){
			FOR(j,0,m-1){
				b[++tn]=mp[i][j];
			}
		} 
		sort(b+1,b+tn+1);tn=unique(b+1,b+tn+1)-b-1;
		FOR(i,0,n-1){
			FOR(j,0,m-1){
				mp[i][j]=lower_bound(b+1,b+tn+1,mp[i][j])-b;
			}
		}*/
		ll ans=0;
		ht.pre();
		FOR(l,0,m-1){
			FOR(r,l,m-1){
				ht.clear();
				FOR(i,0,n-1){
					int utp=ht.work(i,l,r);
					/*if(utp!=-1){
						FOR(p,l,r){
							assert(mp[i][p]==mp[utp][p]);
						}
					}
					printf("%d %d %d %d\n",l,r,i,utp);
					FOR(q,utp+1,i-1){
							int flg=0;
							FOR(p,l,r){
								if(mp[i][p]!=mp[q][p]) flg=1;
							}
							
							assert(flg);
						}*/
					//printf("%d %d %d %d\n",l,r,i,1ll*(i-utp)*(n-i));
					ans+=1ll*(i-utp)*(n-i);
				}
			}
		}
		printf("%lld",ans);
	}
}
namespace sub2{
	int f[N],g[N];
	void solve(){
		ll ans=0;
		FOR(i,0,n-1){
		//	ans+=(m*(m+1)/2);
			FOR(k,0,m-1) f[k]=0;
			for(int j=i-1;j>=0;j--){
				g[0]=(mp[j][0]==mp[i][0]);
				FOR(k,1,m-1){
					if(mp[j][k]==mp[i][k]) g[k]=g[k-1]+1;
					else g[k]=0;
				}
				FOR(k,0,m-1){
					if(g[k]<=f[k]) continue;
					else{
						ans+=1ll*(g[k]-f[k])*(i-j)*(n-i);
						f[k]=g[k];
					}
				}
			}
			FOR(k,0,m-1){
				ans+=1ll*((k+1)-f[k])*(i+1)*(n-i);
			}
		}
		printf("%lld",ans);
	}
}
int main(){
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	n=read(),m=read();
	int flg=0;
	FOR(i,1,n*m){
		int x=read();
		if(x!=i) flg=1;
		mp[(i-1)/m].push_back(x);
	}
	if(!flg){
		ll ans=0;
		FOR(i,1,n){
			FOR(j,1,m){
				ans+=(1ll*(n-i+1)*(n-i+2)/2ll)*(n-j+1);
			}
		}
		printf("%lld",ans);
		return 0;
	}
	int B=sqrt(1ll*n*m);
	if(m<=B){
		sub1::solve();
	}else sub2::solve();
	return 0;
}

然后就不会了orz

看题解,把我们在trie树上的做法改一下,考虑枚举串长度,那么每次长度加一相当于把trie树根的儿子合并作为新的根,用set启发式合并就可以\(nm\log^2n\)了,用splay就是\(nm\log n\)

sequence

注意到以一个点开头的序列按位与的值只有最多30种,把这之中整除k的提出来,现在的问题就是\(n\log n\)个贡献区间,\(q\)个询问区间,满足\(ql\leq l\leq qr\)的要求才能产生贡献,其实\(l\leq qr\)可以去掉,那么排个序+线段树就好了。至于找贡献区间,按数位分类,维护log个指针就好了。

写完了发现写的是按位或,不过差不了多少(事实上按位或处理更复杂)

这场才是温暖场啊,昨天那场不咋温暖

由于我电脑太烂本地跑不过,于是加了O2O3,洛谷测上不开O2随便过

#include<bits/stdc++.h>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#define ll long long
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
using namespace std;
int read(){
	int x=0,pos=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
	return pos?x:-x;
}
const int N = 1e5+200;
int n,q,k;
int a[N];
vector<int> vec[50];
int tp[50];
int t[N],l[N][32],r[N][32];
ll ans[N*5];
struct que{
	int l,r,id;
	que(int l=0,int r=0,int id=0):l(l),r(r),id(id){}
}qi[N*5]; 
int cmp(que a,que b){
	return a.l>b.l;
}
ll d[N*4],tag[N*4];
inline void push_down(int now,int l,int r){
	if(tag[now]){
		int mid=(l+r)>>1;
		d[now<<1]+=tag[now]*(mid-l+1);
		tag[now<<1]+=tag[now];
		d[now<<1|1]+=tag[now]*(r-mid);
		tag[now<<1|1]+=tag[now];
		tag[now]=0;
	}
}
inline void push_up(int now){
	d[now]=d[now<<1]+d[now<<1|1];
}
void modify(int now,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr){
		d[now]+=(r-l+1);tag[now]++;return;
	} 
	int mid=(l+r)>>1;push_down(now,l,r);
	if(ql<=mid) modify(now<<1,l,mid,ql,qr);
	if(mid<qr) modify(now<<1|1,mid+1,r,ql,qr);
	push_up(now);
}
ll query(int now,int l,int r,int ql,int qr){
	if(ql<=l&&r<=qr){
		return d[now];
	}
	int mid=(l+r)>>1;push_down(now,l,r);
	ll res=0;
	if(ql<=mid) res=query(now<<1,l,mid,ql,qr);
	if(mid<qr) res+=query(now<<1|1,mid+1,r,ql,qr);
	return res;
}
void work(int now){
	FOR(i,1,t[now]){
		modify(1,1,n,l[now][i],r[now][i]);
	} 
}
inline void out(ll now){
	if(now>9) out(now/10);
	putchar('0'+now%10);
} 
int main(){
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	n=read(),q=read(),k=read();
	FOR(i,1,n){
		a[i]=read();
		FOR(j,0,30){
			if(!((1<<j)&a[i])) vec[j].push_back(i);
		}
	}
	FOR(i,1,n){
		int c[32],top=0;
		FOR(j,0,30){  ////////////////////change
			if(!((1<<j)&a[i])){
				tp[j]++;
			}else if(tp[j]!=vec[j].size()){
				c[++top]=vec[j][tp[j]];
			}
		}
		sort(c+1,c+top+1);
		int x=a[i],pos=i;
		FOR(j,1,top){
			if(c[j]==c[j-1]) continue;
			if(x%k==0){
				l[i][++t[i]]=pos;
				r[i][t[i]]=c[j]-1;	
			}
			x&=a[c[j]];pos=c[j];
		}
		if(pos<=n&&x%k==0){
			l[i][++t[i]]=pos;
			r[i][t[i]]=n;
		} 
	} 
	//cout<<clock();
	FOR(i,1,q){
		int lx=read(),rx=read();
		qi[i]=que(lx,rx,i);
	} 
	sort(qi+1,qi+q+1,cmp);
	int pos=n;
	FOR(i,1,q){
		while(pos>=1&&pos>=qi[i].l){
			work(pos);
			pos--;
		}
		ans[qi[i].id]=query(1,1,n,qi[i].l,qi[i].r);
	}
	FOR(i,1,q){
		out(ans[i]);putchar(10);
	}
	return 0;
}

permutation

推了挺久的,考场上不太做得出来(还是题做得不够)

先考虑最大的数放在哪,接下来两边互不影响,即有a的贡献一定在左边,给b的贡献一定在右边,如果此时知道左右的方案数就可以O(n)算,于是我们现在只用考虑一边

\(f_{j,i}\)表示jm的数有 \(j\) 个,一共有 \(i\) 个数,同样枚举最大的在哪个位置,有转移

\[f_{j,i}=\sum_{k=j}^{i-1} f_{j-1,k}{i\choose k} \]

其实可以扩大范围,因为不满足条件的为0:

\[f_{j,i}=\sum_{k=0}^{i-1} f_{j-1,k}\frac{i!}{k!(i-k)!} \]

这可以n^2log,然后就不会了...

看题解,考虑组合意义,\(f_{j,i}=\left[^i_j\right]\),因为每一个被记入答案的位置的后面几个数一定可以互换。那么求出一行就行了。

posted @ 2020-06-18 21:30  lcyfrog  阅读(269)  评论(0编辑  收藏  举报