「模拟赛40」题解

我好篛啊

T1:进化

沙雕题,(没人发现我写的是错解)

一开始看错题耗了一个多小时导致T4暴力打挂

考虑每个答案:

0 0:有连续的1,或有其他字符,或t的个数大于l的个数+1

1 1:无连续的1且首字母是l

0 1:无连续的1且首字母是t

1 0:正经人都知道没有这情况

T2:皇室战争

沙雕题,考场写了两个小时的(O(n^2)),大样例一直不过,直接转(O(n^3))

定义状态 (f_{i,j}) 表示 (i,j) 内是否内消完

[f_{i,j} | =f_{i,k} 与 f_{k+1,j} ]

枚举 (k)(O(n^3)),实际上 (f_{i,j}) 只会从 (f_{i+2,j}),(f_{i+1,j-1}),(f_{i,j-2}) 转移

定义 (g_{i}) 表示以 (i) 为结尾的答案最大值,然后就是常见的分组DP了

[g_i=g_{i-1} ]

[g_i=max(g_j+sum_i-sum_j)(f_{i,j}==1) ]

T3:MC

考场调到最后发现所有map的insert不能合并两个map,然后就无了

正解就是经典的时光倒流,合并两个桶

线段树合并 或 启发式合并+map

#include<bits/stdc++.h>
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define rint register int 
#define ll long long 
using namespace std;
const int maxn=1e6+5,INF=0x3f3f3f3f,mol=19260817;
inline int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
    return s*w;
}
int n,m,k,f[maxn],tot,ans[maxn],dcol,black[maxn],summm,summ,cnt[maxn],siz[maxn];
ll c[maxn*2],ny[maxn*2];
unordered_map<int,int> sum[maxn];
unordered_map<int,int>::iterator it;
struct Edge{
    int from,to;
}e[maxn];
struct Query{
    int opt,id,x,y,z;
    bool operator < (const Query &A)const{
        return id>A.id;
    }
}q[maxn];
inline ll qpow(ll _,ll __,ll ___=1){
    while(__){
        if(__&1)___=___*_%mol;
        _=_*_%mol;
        __>>=1;
    }
    return ___;
}
void Init(){
    c[0]=1;
    int maxe=1e6;
    for(rint i=1;i<=n;++i)f[i]=i,siz[i]=1;
    for(rint i=1;i<=maxe;++i)c[i]=c[i-1]*i%mol;
    ny[maxe]=qpow(c[maxe],mol-2);
    for(rint i=maxe-1;i>=0;i--)ny[i]=ny[i+1]*(i+1)%mol;
}
inline ll C(rint _,rint __){
    if(!__)return 1;
    return c[_]*ny[__]%mol*ny[_-__]%mol;
}
inline int Find(rint x){
    while(f[x]!=x)x=f[x];
    return x;
}
inline void Merge(rint u,rint v){
    u=Find(u);v=Find(v);
    if(u==v)return;
    if(siz[u]>siz[v]){
        f[v]=u;
        cnt[u]+=cnt[v];
        siz[u]+=siz[v];
        for(it=sum[v].begin();it!=sum[v].end();it++)sum[u][(*it).first]+=(*it).second;
    }else{
        f[u]=v;
        cnt[v]+=cnt[u];
        siz[v]+=siz[u];
        for(it=sum[u].begin();it!=sum[u].end();it++)sum[v][(*it).first]+=(*it).second;
    }
}
signed main(){
    freopen("mc.in","r",stdin);
    freopen("mc.out","w",stdout);
    n=read(),m=read(),k=read();Init();
    for(rint i=1,x,y;i<=n;++i)x=read(),y=read(),sum[i][y]=cnt[i]=x;
    for(rint i=1,x,y;i<=m;++i)e[i].from=read(),e[i].to=read();
    for(rint i=1,opt,x,y,z;i<=k;++i){
        opt=read();q[i].opt=opt;q[i].id=i;
        if(opt==1){
            x=read(),y=read(),z=read();
            cnt[x]+=y;sum[x][z]+=y;
            q[i].x=x,q[i].y=y,q[i].z=z;
        }else if(opt==2){
            x=read();black[x]++;
            q[i].x=x;
        }else{
            x=read(),y=read(),z=read();summ=summm=0;q[i].x=x,q[i].y=y,q[i].z=z;
        }
    }
    sort(q+1,q+1+k);
    for(rint i=1;i<=m;++i){
        if(black[i])continue;
        Merge(e[i].from,e[i].to);
    }
    for(rint i=1;i<=k;++i){
        int opt=q[i].opt,x=q[i].x,y=q[i].y,z=q[i].z;
        if(opt==1){
            int rt=Find(x);
            cnt[rt]-=y;sum[rt][z]-=y;
        }else if(opt==2){
            if(--black[x]==0)Merge(e[x].from,e[x].to);
        }else{
            int rt=Find(x);
            summ=sum[rt][z],summm=cnt[rt],tot++;
            if(summ>=y)ans[tot]=C(summ,y)*qpow(C(summm,y),mol-2)%mol;
        }
    }
    for(rint i=tot;i>=1;--i)printf("%d
",ans[i]);
    return 0;
}

T4:简单题

考场用仅剩的几分钟推出了错误的结论,成功暴零(为啥不给样例解释)

容易发现 (E(u,v)) 的答案是无视这条边,跑最小生成树,两个点被合并时的边权

直接做是 (O(n^mlog(m)))

考虑树边和非树边对答案的影响

非树边的答案是最小生成树上两点间的最大值

树边的答案是 (u)(v) 所有非树边的最小值

都可以写树刨,注意由于边化点的边界问题



#include<bits/stdc++.h>
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define rint register int 
#define ll long long 
using namespace std;
const int maxn=1e6+5,INF=1e9;
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int n,m,k,f[maxn],tot,head[maxn],dfn[maxn],dclock,par[maxn],depth[maxn],rhead[maxn],tott,top[maxn],siz[maxn],son[maxn],a[maxn],b[maxn],black[maxn];
ll dis[maxn],ans[maxn];
struct Edge{
	int next,from,to,id;
	ll dis;
	bool operator <(const Edge &A)const{
		return dis<A.dis;
	}
}e[maxn],re[maxn];
inline void Add(rint x,rint y,register ll z){e[++tot]=(Edge){head[x],x,y,tot,z};head[x]=tot;}
inline void rAdd(rint x,rint y,register ll z){re[++tott]=(Edge){rhead[x],x,y,tott,z};rhead[x]=tott;}
void Init(){
	for(rint i=1;i<=n;++i)f[i]=i;
}
int Find(int x){
	if(f[x]==x)return x;
	return f[x]=Find(f[x]);
}
void DFS1(int u,int fa){
	par[u]=fa;siz[u]=1;
	for(int x=rhead[u];x;x=re[x].next){
		int v=re[x].to;
		if(v==fa)continue;
		depth[v]=depth[u]+1;dis[v]=dis[u]+re[x].dis;
		a[v]=re[x].dis;
		DFS1(v,u);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v])son[u]=v;
	}
}
void DFS2(int u,int t){
	top[u]=t;dfn[u]=++dclock;b[dfn[u]]=a[u];
	if(son[u])DFS2(son[u],t);
	for(int x=rhead[u];x;x=re[x].next){
		int v=re[x].to;
		if(v==par[u]||v==son[u])continue;
		DFS2(v,v);
	}
}
struct Segment_Tree{
	int l,r,minn,maxx,lazy,siz;
}tree[maxn*4];
#define mid ((l+r)>>1)
void pushup1(int rt){tree[rt].minn=Min(tree[rt*2].minn,tree[rt*2+1].minn);}
void pushup2(int rt){tree[rt].maxx=Max(tree[rt*2].maxx,tree[rt*2+1].maxx);}
void Build(int rt,int l,int r){
	tree[rt].l=l,tree[rt].r=r,tree[rt].siz=r-l+1,tree[rt].maxx=0,tree[rt].minn=INF;
	if(l==r)return tree[rt].maxx=b[l],void();
	Build(rt*2,l,mid);Build(rt*2+1,mid+1,r);
	pushup2(rt);	
}
void update(int rt,int w){
	if(tree[rt].minn>w)tree[rt].minn=w;
	if(tree[rt].lazy>w||!tree[rt].lazy)tree[rt].lazy=w;
}
void pushdown(int rt){
	if(tree[rt].lazy)update(rt*2,tree[rt].lazy),update(rt*2+1,tree[rt].lazy),tree[rt].lazy=0;
}
void modify(int rt,int s,int t,int w){
	int l=tree[rt].l,r=tree[rt].r;
	if(s<=l&&r<=t)return update(rt,w),void();
	pushdown(rt);
	if(s<=mid)modify(rt*2,s,t,w);
	if(mid<t)modify(rt*2+1,s,t,w);
	pushup1(rt);
}
int query1(int rt,int s,int t){
	int l=tree[rt].l,r=tree[rt].r,now=INF;
	if(s<=l&&r<=t)return tree[rt].minn;
	pushdown(rt);
	if(s<=mid)now=min(now,query1(rt*2,s,t));
	if(mid<t)now=min(now,query1(rt*2+1,s,t));
	return now;
}
int query2(int rt,int s,int t){
	int l=tree[rt].l,r=tree[rt].r,now=0;
	if(s<=l&&r<=t)return tree[rt].maxx;
	pushdown(rt);
	if(s<=mid)now=max(now,query2(rt*2,s,t));
	if(mid<t)now=max(now,query2(rt*2+1,s,t));
	return now;
}
void modifymin(int u,int v,int w){
	while(top[u]!=top[v]){
		if(dfn[top[u]]>dfn[top[v]])modify(1,dfn[top[u]],dfn[u],w),u=par[top[u]];
		else modify(1,dfn[top[v]],dfn[v],w),v=par[top[v]];
	}
	if(dfn[v]>dfn[u])modify(1,dfn[u]+1,dfn[v],w);
	else modify(1,dfn[v]+1,dfn[u],w);
}
int querymax(int u,int v,int now=0){
	while(top[u]!=top[v]){
		if(dfn[top[u]]>dfn[top[v]])now=max(now,query2(1,dfn[top[u]],dfn[u])),u=par[top[u]];
		else now=max(now,query2(1,dfn[top[v]],dfn[v])),v=par[top[v]];
	}
	if(dfn[v]>dfn[u])now=max(now,query2(1,dfn[u]+1,dfn[v]));
	else now=max(now,query2(1,dfn[v]+1,dfn[u]));
	return now;
}
int querymin(int u,int v,int now=INF){
	while(top[u]!=top[v]){
		if(dfn[top[u]]>dfn[top[v]])now=min(now,query1(1,dfn[top[u]],dfn[u])),u=par[top[u]];
		else now=min(now,query1(1,dfn[top[v]],dfn[v])),v=par[top[v]];
	}
	if(dfn[v]>dfn[u])now=min(now,query1(1,dfn[u]+1,dfn[v]));
	else now=min(now,query1(1,dfn[v]+1,dfn[u]));
	return now;
}
int main(){
	freopen("easy.in","r",stdin);
	freopen("easy.out","w",stdout);
	n=read(),m=read();Init();
	for(rint i=1,x,y,z;i<=m;++i)x=read(),y=read(),z=read(),Add(x,y,z);
	sort(e+1,e+1+tot);
	for(rint i=1;i<=tot;i++){
		int u=e[i].from,v=e[i].to,rt1=Find(u),rt2=Find(v);
		if(rt1!=rt2)rAdd(u,v,e[i].dis),rAdd(v,u,e[i].dis),f[rt2]=rt1,black[i]=1;
	}
	DFS1(1,1);DFS2(1,0);Build(1,1,n);
	for(rint i=1;i<=tot;i++){
		if(!black[i])modifymin(e[i].from,e[i].to,e[i].dis);
	}
	for(rint i=1;i<=tot;i++){
		if(!black[i])ans[e[i].id]=querymax(e[i].from,e[i].to);
		else ans[e[i].id]=querymin(e[i].from,e[i].to);
	}
	for(rint i=1;i<=tot;i++)printf("%lld
",ans[i]);
	return 0;
}

原文地址:https://www.cnblogs.com/614685877--aakennes/p/14044702.html