省选测试30

A. 要换换名字

分析

暴力的做法就是二分一个长度 (len)

每一个字符串向所有长度小于等于 (len) 的子序列连边

然后用 (dinic) 跑一个二分图最大匹配即可

如果能匹配到n个就返回 (1)

否则返回 (0)

发现如果一个字符串有多于 (n) 个子序列,那么一定能匹配

所以只需要 (bfs) 找出长度前 (n) 小的子序列即可

分析

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<map>
#include<queue>
#include<string>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=2e5+5,maxm=305;
char ss[maxm][maxm];
int n,len[maxm],nxt[maxm][maxm][28],cnt,l,r,mids;
std::map<std::string,int> mp;
std::string rk[maxn];
struct jie{
	int len,id;
	std::string val;
	jie(){}
	jie(rg int aa,rg int bb,std::string cc){
		len=aa,id=bb,val=cc;
	}
};
std::vector<jie> g[maxm];
void init(rg int id){
	std::queue<jie> q;
	std::string tmp;
	for(rg int i=0;i<26;i++){
		rg int now=nxt[id][0][i];
		tmp='a'+i;
		if(now){
			q.push(jie(1,now,tmp));
		}
	}
	rg int ncnt=0;
	while(!q.empty()){
		rg int tmp1=q.front().id,tmp2=q.front().len;
		std::string tmp3=q.front().val;
		q.pop();
		if(!mp[tmp3]){
			mp[tmp3]=++cnt;
			rk[cnt]=tmp3;
		}
		g[id].push_back(jie(tmp2,mp[tmp3],tmp3));
		ncnt++;
		if(ncnt>=n) return;
		for(rg int i=0;i<26;i++){
			if(nxt[id][tmp1][i]){
				tmp=tmp3+ss[id][nxt[id][tmp1][i]];
				q.push(jie(tmp2+1,nxt[id][tmp1][i],tmp));
			}
		}
	}
}
int h[maxn],tot=2,mmax,h2[maxn],dep[maxn],q[maxn],head,tail,s,t;
struct asd{
	int to,nxt,val;
}b[maxn];
void ad(int aa,int bb,int cc){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	b[tot].val=cc;
	h[aa]=tot++;
}
bool bfs(){
	for(rg int i=0;i<=mmax;i++){
		dep[i]=0;
		h[i]=h2[i];
	}
	q[head=tail=1]=s;
	dep[s]=1;
	while(head<=tail){
		rg int now=q[head++];
		for(rg int i=h[now];i!=-1;i=b[i].nxt){
			rg int u=b[i].to;
			if(!dep[u] && b[i].val){
				dep[u]=dep[now]+1;
				q[++tail]=u;
			}
		}
	}
	return dep[t];
}
int dfs(int now,int ac1){
	if(now==t) return ac1;
	int ac2=0;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		h[now]=i;
		rg int u=b[i].to;
		if(dep[u]==dep[now]+1 && b[i].val){
			rg int nans=dfs(u,std::min(ac1,b[i].val));
			ac1-=nans;
			ac2+=nans;
			b[i].val-=nans;
			b[i^1].val+=nans;
		}
		if(ac1==0) break;
	}
	if(ac2==0) dep[now]=0;
	return ac2;
}
int vis[maxn],tim;
bool jud(rg int val){
	memset(h,-1,sizeof(h));
	tot=2,s=cnt+n+1,t=cnt+n+2;
	for(rg int i=1;i<=n;i++){
		ad(s,i+cnt,1),ad(i+cnt,s,0);
	}
	tim++;
	for(rg int i=1;i<=n;i++){
		for(rg int j=0;j<g[i].size();j++){
			if(g[i][j].len<=val){
				rg int now=g[i][j].id;
				if(vis[now]!=tim) ad(now,t,1),ad(t,now,0);
				ad(i+cnt,now,1),ad(now,i+cnt,0);
				vis[now]=tim;
			}
		}
	}
	mmax=t;
	for(rg int i=1;i<=mmax;i++) h2[i]=h[i];
	rg int ans=0;
	while(bfs()) ans+=dfs(s,1e9);
	return ans==n;
}
int main(){
	n=read();
	for(rg int i=1;i<=n;i++) scanf("%s",ss[i]+1);
	for(rg int i=1;i<=n;i++) len[i]=strlen(ss[i]+1);
	for(rg int i=1;i<=n;i++){
		for(rg int j=0;j<=len[i];j++){
			for(rg int k=j+1;k<=len[i];k++){
				rg int tmp=ss[i][k]-'a';
				if(!nxt[i][j][tmp]){
					nxt[i][j][tmp]=k;
				}
			}
		}
	}
	for(rg int i=1;i<=n;i++) init(i);
	for(rg int i=1;i<=n;i++) r=std::max(r,len[i]);
	l=1;
	while(l<=r){
		mids=(l+r)>>1;
		if(jud(mids)) r=mids-1;
		else l=mids+1;
	}
	if(!jud(l)) printf("-1
");
	else {
		printf("%d
",l);
		for(rg int i=1;i<=n;i++){
			for(rg int j=h[i+cnt];j!=-1;j=b[j].nxt){
				if(b[j].val==0){
					std::cout<<rk[b[j].to]<<std::endl;
					break;
				}
			}
		}
	}
	return 0;
}

B. 动态半平面交

分析

貌似强制在线的做法空间复杂度是 (log^2n)的,所以这道题没有强制在线

首先把一个质数 (p) 拆成 (p,p^2,p^3 cdots p^k),每一个对答案的贡献都是 (p)

考虑如何去掉重复的贡献

在序列上的做法是记录一个(pre[x]) 表示 (x) 上一次出现的位置

当把新的 (x) 加入的时候把 (pre[x]) 的贡献减掉即可

把这个问题搬到树上,就是把所有的节点按照 (dfn) 序从小到大排序,把相邻两个点之间 (lca) 的贡献去掉

之所以可以这么做是因为我们查询的是整个子树的贡献,它的 (dfn) 序是连续的

因为是动态的所以要拿 (set) 维护

然后把所有询问按照深度从小到大排序,同时把所有的点按照深度从小到大排序依次加入

维护一棵以 (dfn) 序为下标的线段树

那么要进行的操作就是单点修改和区间查询

时间复杂度是 (O(nlog^2n))

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<set>
#include<vector>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e5+5,mod=998244353;
inline int addmod(rg int now1,rg int now2){
	return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
	return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
	return now1*=now2,now1>=mod?now1%mod:now1;
}
inline int ksm(rg int ds,rg int zs){
	rg int nans=1;
	while(zs){
		if(zs&1) nans=mulmod(nans,ds);
		ds=mulmod(ds,ds);
		zs>>=1;
	}
	return nans;
}
int h[maxn],tot=1,n,q;
struct asd{
	int to,nxt;
}b[maxn<<1];
void ad(rg int aa,rg int bb){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	h[aa]=tot++;
}
int fa[maxn],siz[maxn],dep[maxn],son[maxn];
void dfs1(rg int now,rg int lat){
	fa[now]=lat;
	dep[now]=dep[lat]+1;
	siz[now]=1;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		dfs1(u,now);
		siz[now]+=siz[u];
		if(son[now]==0 || siz[u]>siz[son[now]]) son[now]=u;
	}
}
int tp[maxn],rk[maxn],dfn[maxn],dfnc;
void dfs2(rg int now,rg int top){
	dfn[now]=++dfnc;
	tp[now]=top;
	rk[dfnc]=now;
	if(son[now]) dfs2(son[now],top);
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==son[now] || u==fa[now]) continue;
		dfs2(u,u);
	}
}
int getlca(rg int xx,rg int yy){
	while(tp[xx]!=tp[yy]){
		if(dep[tp[xx]]<dep[tp[yy]]) std::swap(xx,yy);
		xx=fa[tp[xx]];
	}
	return dep[xx]<dep[yy]?xx:yy;
}
struct tree{
	int l,r,val;
}tr[maxn<<2];
void push_up(rg int da){
	tr[da].val=mulmod(tr[da<<1].val,tr[da<<1|1].val);
}
void build(rg int da,rg int l,rg int r){
	tr[da].l=l,tr[da].r=r,tr[da].val=1;
	if(l==r) return;
	rg int mids=(l+r)>>1;
	build(da<<1,l,mids),build(da<<1|1,mids+1,r);
}
void xg(rg int da,rg int wz,rg int val){
	if(tr[da].l==tr[da].r){
		tr[da].val=mulmod(tr[da].val,val);
		return;
	}
	rg int mids=(tr[da].l+tr[da].r)>>1;
	if(wz<=mids) xg(da<<1,wz,val);
	else xg(da<<1|1,wz,val);
	push_up(da);
}
int cx(rg int da,rg int l,rg int r){
	if(tr[da].l>=l && tr[da].r<=r) return tr[da].val;
	rg int mids=(tr[da].l+tr[da].r)>>1,nans=1;
	if(l<=mids) nans=mulmod(nans,cx(da<<1,l,r));
	if(r>mids) nans=mulmod(nans,cx(da<<1|1,l,r));
	return nans;
}
int a[maxn],sta[maxn*20],stacnt,id[maxn];
struct longdie{
	int val,num;
	longdie();
	longdie(rg int aa,rg int bb){
		val=aa,num=bb;
	}
};
std::vector<longdie> g[maxn];
void divid(rg int id,rg int val){
	for(rg int i=2;i*i<=val;i++){
		if(val%i==0){
			rg int now=1;
			while(val%i==0){
				now*=i,val/=i;
				g[id].push_back(longdie(i,now));
				sta[++stacnt]=now;
			}
		}
	}
	if(val>1){
		g[id].push_back(longdie(val,val));
		sta[++stacnt]=val;
	}
}
int ans[maxn];
struct jie{
	int maxdep,num,id;
	jie(){}
	jie(rg int aa,rg int bb,rg int cc){
		maxdep=aa,num=bb,id=cc;
	}
}c[maxn];
bool cmp(rg jie aa,rg jie bb){
	return aa.maxdep<bb.maxdep;
}
bool cmp2(rg int aa,rg int bb){
	return dep[aa]<dep[bb];
}
std::set<int> s[maxn*20];
#define sit std::set<int>::iterator
void updat(rg int num){
	rg int aa,bb,cc;
	rg sit it1,it2,it3;
	for(rg int i=0;i<g[num].size();i++){
		aa=g[num][i].val,bb=g[num][i].num;
		xg(1,dfn[num],aa);
		s[bb].insert(dfn[num]);
		if(s[bb].size()!=1){
			it1=it2=it3=s[bb].lower_bound(dfn[num]);
			if(it1==s[bb].begin()){
				++it2;
				cc=getlca(num,rk[*it2]);
				xg(1,dfn[cc],ksm(aa,mod-2));
			} else if(++it3==s[bb].end()){
				--it2;
				cc=getlca(num,rk[*it2]);
				xg(1,dfn[cc],ksm(aa,mod-2));
			} else {
				++it2,--it1;
				cc=getlca(num,rk[*it1]);
				xg(1,dfn[cc],ksm(aa,mod-2));
				cc=getlca(num,rk[*it2]);
				xg(1,dfn[cc],ksm(aa,mod-2));
				cc=getlca(rk[*it1],rk[*it2]);
				xg(1,dfn[cc],aa);
			}
		}
	}
}
int main(){
	memset(h,-1,sizeof(h));
	n=read(),n=read();
	for(rg int i=1;i<=n;i++) a[i]=read();
	for(rg int i=1;i<=n;i++) divid(i,a[i]);
	std::sort(sta+1,sta+1+stacnt);
	stacnt=std::unique(sta+1,sta+1+stacnt)-sta-1;
	for(rg int i=1;i<=n;i++){
		for(rg int j=0;j<g[i].size();j++){
			g[i][j].num=std::lower_bound(sta+1,sta+1+stacnt,g[i][j].num)-sta;
		}
	}
	rg int aa,bb;
	for(rg int i=1;i<n;i++){
		aa=read(),bb=read();
		ad(aa,bb),ad(bb,aa);
	}
	dfs1(1,0),dfs2(1,1);
	build(1,1,n);
	for(rg int i=1;i<=n;i++) id[i]=i;
	std::sort(id+1,id+n+1,cmp2);
	q=read();
	for(rg int i=1;i<=q;i++){
		aa=read(),bb=read();
		c[i]=jie(std::min(dep[aa]+bb,n),aa,i);
	}
	std::sort(c+1,c+q+1,cmp);
	rg int head=1,now=1;
	for(rg int nowdep=1;nowdep<=n;nowdep++){
		while(now<=n && dep[id[now]]==nowdep) updat(id[now++]);
		while(head<=q && c[head].maxdep==nowdep){
			ans[c[head].id]=cx(1,dfn[c[head].num],dfn[c[head].num]+siz[c[head].num]-1);
			head++;
		}
	}
	for(rg int i=1;i<=q;i++) printf("%d
",ans[i]);
	return 0;
}

C. 获取名额

分析

要求的式子就是 (1-prodlimits_{i=l}^r(1-frac{a_i}{x}))

发现 (prodlimits_{i=l}^r(1-frac{a_i}{x})=exp(sumlimits_{i=l}^rln(1-frac{a_i}{x})))

对于 (ln(1-x)) 进行泰勒展开可以得到 (-sumlimits_{i=1}^{+infty}frac{x^j}{j})

但是发现当 (1-x) 过小时(ln) 的绝对值过大,容易丢精

所以设一个阈值,(1-x) 小于这个值的时候暴力算,否则用泰勒展开的前 (30)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=6e5+5;
int n,q,l,r,wz[maxn][25],lg[maxn];
double a[maxn],val[maxn][25],mmax,ans,x,pre[maxn][32];
void solve(rg int l,rg int r){
	if(l>r) return;
	if(ans<1e-12) return;
	rg int k=lg[r-l+1];
	rg int mids=wz[l][k];
	if(val[l][k]<val[r-(1<<k)+1][k]) mids=wz[r-(1<<k)+1][k];
	rg double val=a[mids]/x;
	if(val>0.4){
		ans*=(1-val);
		solve(l,mids-1),solve(mids+1,r);
	} else {
		rg double sum=0,tmp=x;
		for(rg int i=1;i<=30;i++){
			sum+=(pre[r][i]-pre[l-1][i])/tmp;
			tmp*=x;
		}
		ans*=exp(-sum);
	}
}
int main(){
	n=read(),q=read();
	for(rg int i=1;i<=n;i++) a[i]=read(),mmax=std::max(mmax,a[i]);
	for(rg int i=1;i<=n;i++) a[i]/=mmax;
	for(rg int i=1;i<=n;i++) wz[i][0]=i,val[i][0]=a[i];
	for(rg int j=1;j<=20;j++){
		for(rg int i=1;i+(1<<j)-1<=n;i++){
			if(val[i][j-1]<val[i+(1<<(j-1))][j-1]) wz[i][j]=wz[i+(1<<(j-1))][j-1];
			else wz[i][j]=wz[i][j-1];
			val[i][j]=std::max(val[i][j-1],val[i+(1<<(j-1))][j-1]);
		}
	}
	for(rg int i=1;i<=n;i++){
		rg double tmp=a[i];
		for(rg int j=1;j<=30;j++){
			pre[i][j]=pre[i-1][j]+tmp/j;
			tmp*=a[i];
		}
	}
	for(rg int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
	for(rg int i=1;i<=q;i++){
		l=read(),r=read(),x=read();
		ans=1,x/=mmax;
		solve(l,r);
		printf("%.10f
",1.0-ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/liuchanglc/p/14448605.html