「模拟赛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\n",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\n",ans[i]);
return 0;
}