ZR 20省选集训Day4

http://zhengruioi.com/contest/568

颓狗来更博了;

A.Manager

送分题,由于只改变了一个数,而且是把这个数变成最大值,所以对于中位数的影响很小;如果中位数是\(w\),那么被修改的\(a_i\leq w\),那么新的中位数就是原来第\(\lfloor \frac{k+1}{2}\rfloor +1\)小的数,否则就不变;搞一个线段树合并,求一下每个子树第\(\lfloor \frac{k+1}{2}\rfloor\)和第\(\lfloor \frac{k+1}{2}\rfloor +1\)小的数,显然一个修改只能影响根到这个点路径上的数,于是搞一个树状数组求一下这个点到根的路径上哪些点的中位数大于等于\(a_i\)即可。

代码

#include<bits/stdc++.h>
#define re register
#define LL long long
#define lb(x) (x&-x)
#define max(a,b) ((a)>(b)?(a):(b))
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=2e5+5,M=maxn*20;
struct E{int v,nxt;}e[maxn];
int l[M],r[M],d[M],m,rt[maxn],cnt;LL ans,c[maxn],h[maxn];
int n,a[maxn],head[maxn],sz[maxn],g[maxn],f[maxn],num;
inline void _add(int x,int y) {
	e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
int ins(int nw,int x,int y,int pos) {
	if(!nw) nw=++cnt;d[nw]++;
	if(x==y)return nw;
	int mid=x+y>>1;
	if(pos<=mid) l[nw]=ins(l[nw],x,mid,pos);
	else r[nw]=ins(r[nw],mid+1,y,pos);
	return nw;
}
int merge(int a,int b,int x,int y) {
	if(!a||!b) return a+b;
	if(x==y) {d[a]+=d[b];return a;}
	int mid=x+y>>1;
	l[a]=merge(l[a],l[b],x,mid),r[a]=merge(r[a],r[b],mid+1,y);
	d[a]=d[l[a]]+d[r[a]];return a;
}
int fid(int nw,int x,int y,int k) {
	if(x==y)return x;
	int mid=x+y>>1;
	if(d[l[nw]]>=k) return fid(l[nw],x,mid,k);
	return fid(r[nw],mid+1,y,k-d[l[nw]]);
}
void dfs(int x) {
	rt[x]=ins(rt[x],1,m,a[x]);sz[x]=1;
	for(re int i=head[x];i;i=e[i].nxt) 
		dfs(e[i].v),sz[x]+=sz[e[i].v],merge(rt[x],rt[e[i].v],1,m);
	f[x]=fid(rt[x],1,m,(sz[x]+1)>>1);
	if(sz[x]==1) g[x]=100000;
	else g[x]=fid(rt[x],1,m,((sz[x]+1)>>1)+1);
}
inline void add(int x,int v) {
	for(re int i=x;i;i-=lb(i))c[i]+=v;
}
inline LL ask(int x) {
	LL nw=0;for(re int i=x;i<=m;i+=lb(i))nw+=c[i];return nw;
}
void Dfs(int x) {
	add(f[x],g[x]-f[x]),h[x]=ans+ask(a[x]);
	for(re int i=head[x];i;i=e[i].nxt) Dfs(e[i].v);
	add(f[x],f[x]-g[x]);
}
int main() {
	n=read();for(re int i=1;i<=n;i++)a[i]=read(),m=max(a[i],m);
	for(re int x,i=2;i<=n;i++) x=read(),_add(x,i);
	dfs(1);for(re int i=1;i<=n;i++)ans+=f[i];
	Dfs(1);for(re int i=1;i<=n;i++)printf("%lld\n",h[i]);
	return 0;
}

B.GCD再放

首先对于每一个序列单独求一下所有区间\(\gcd\)的和,经典问题,利用前缀\(\gcd\)变化次数为\(\log\)可以在\(O(n\log n)\)的时间内解决。

考虑对于每一个\(i\)求一下有多少个区间满足\(\gcd\)\(i\),可能不太好求,于是反演,转化为求有多少个区间满足\(\gcd\)\(i\)的倍数。

考虑这\(n\)个序列拼接起来,我们没有计算的情况是跨过多个序列的,可以视为某个序列的一个后缀拼接上几个完整的序列再拼上另一个序列的前缀,如果我们当前在计算\(\gcd\)\(i\)的倍数的时的答案,我们钦定了一个满足要求的前缀和一个满足要求的后缀,我们要选一些完整序列在中间,设满足要求的完整序列有\(k\)个,那么方案数就是
\(\sum_{i=0}^n\binom{k}{i}i!(n-i-1)!=k!\sum_{i=0}^k\frac{(n-i-1)!}{(k-i)!}=k!(n-k-1)!\sum_{i=0}^k\binom{n-1-i}{n-1-k}=k!(n-k-1)!\binom{n}{n-k}=\frac{n!}{n-k}\)

式子化得可能有些草率,但中间几步都是经典的转化,就不赘述了。

#include<bits/stdc++.h>
#define re register
#define pb push_back
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int mod=1e9+7,maxn=1e5+5;
std::vector<int> d[maxn],pre[maxn],beh[maxn];
int gcd(int a,int b) {return !b?a:gcd(b,a%b);}
inline int dqm(int x) {return x<0?x+mod:x;}
inline int qm(int x) {return x>=mod?x-mod:x;}
inline int upd(int &x,int y) {x+=y;if(x>=mod)x-=mod;}
int n,tot,lim,f[maxn],p[maxn>>1],c[maxn],g[maxn],a[maxn];
int fac[maxn],inv[maxn],st[maxn],sz[maxn],b[maxn],tax[maxn],bl[maxn];
inline int solve(int *a,int n) {
	int top=0,ans=0,pre,nsz;
	for(re int i=1;i<=n;i++) {
		st[++top]=a[i];sz[top]=1;
		for(re int j=top-1;j;--j)st[j]=gcd(st[j],a[i]);
		pre=top,top=0,nsz=0;st[pre+1]=0;
		for(re int j=1;j<=pre;++j) {
			nsz+=sz[j];
			if(st[j]!=st[j+1])
				st[++top]=st[j],sz[top]=nsz,nsz=0;
		}
		for(re int j=1;j<=top;j++)
			upd(ans,1ll*sz[j]*st[j]%mod);
	}
	while(top)st[top]=sz[top]=0,--top;
	return ans;
}
inline void init(int N) {
	for(re int i=2;i<=N;i++) {
		if(!f[i]) p[++p[0]]=i;
		for(re int j=1;j<=p[0]&&p[j]*i<=N;++j) {
			f[p[j]*i]=1;if(i%p[j]==0)break;
		}
	}
	for(re int i=1;i<=N;i++)	
		for(re int j=i;j<=N;j+=i)d[j].pb(i);
}
inline int calc(int k) {
	return (k+2>n||k<0)?0:1ll*fac[n]*inv[n-k]%mod;
}
inline void ins(int id,int v) {
	for(re int i=0;i<beh[id].size();++i) {
		int x=beh[id][i],y=gcd(x,b[id]);
		for(re int j=0;j<d[x].size();++j) tax[d[x][j]]+=v;
		for(re int j=0;j<d[y].size();++j) bl[d[y][j]]+=v;
	}
}
inline void solve(int h) {
	upd(g[h],1ll*calc(c[h]-1)*bl[h]%mod);
	upd(g[h],1ll*calc(c[h])*dqm(tax[h]-bl[h])%mod);
}
int main() {
	n=read();fac[0]=inv[1]=1;
	for(re int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod;
	for(re int i=2;i<=n;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(re int nw,k,i=1;i<=n;i++) {
		k=read();nw=0;
		for(re int j=1;j<=k;++j)a[j]=read(),lim=max(lim,a[j]);
		upd(tot,1ll*solve(a,k)*fac[n]%mod);
		for(re int j=k;j;--j) beh[i].pb(nw=gcd(a[j],nw));
		for(re int j=1;j<=k;++j) pre[i].pb(b[i]=gcd(a[j],b[i]));
	}
	init(lim);
	for(re int i=1;i<=n;i++)c[b[i]]++,ins(i,1);
	for(re int i=1;i<=p[0];++i) 
		for(re int j=lim/p[i];j;--j) c[j]+=c[j*p[i]];
	for(re int i=1;i<=n;i++) {
		ins(i,-1);
		for(re int j=0;j<d[b[i]].size();++j) c[d[b[i]][j]]--;
		for(re int j=0;j<pre[i].size();++j) {
			int x=pre[i][j];
			for(re int k=0;k<d[x].size();++k) solve(d[x][k]);
		}
		ins(i,1);
		for(re int j=0;j<d[b[i]].size();++j) c[d[b[i]][j]]++;
	}
	for(re int i=1;i<=p[0];++i) 
		for(re int j=1;j<=lim/p[i];j++) g[j]=dqm(g[j]-g[j*p[i]]);
	for(re int i=1;i<=lim;++i)upd(tot,1ll*g[i]*i%mod);
	printf("%d\n",tot);
	return 0;
}
posted @ 2020-03-14 21:12  asuldb  阅读(33)  评论(0编辑  收藏  举报