test201909027 老Z

30+100+40=170。数据出锅*2,也是没谁了。

装饰

快要到 Mope 的生日了,Mope 希望举行一场盛大的生日派对。

派对的准备工作中当然有装饰房间啦。Mope 的房间里有按从左到右的顺序排好的 n 个装饰品,其中第 i 个装饰品被挂在高为 ai 的地方。Mope 不希望改变它们的位置,所以他决定取下一些饰品,设留下来的序列为{bi},他希望任意相邻三项 bi, bi+1, bi+2 满足bi ≤ bi+1 ≤ bi+2或者bi ≥ bi+1 ≥ bi+2。Mope 希望知道他至少要取下多少个饰品。

对于所有测试点:1 ≤ n ≤ 200,000, 1 ≤ ai ≤ 1,000,000,000

题解

维护三种 DP 数组,fi 表示以高度 i 为结尾的最后两项是严格上升的最长子序列长度,gi 表示以高度 i 为结尾的最后两项是严格下降的最长子序列长度,hi 表示以高度 i 为结尾的最后两项是严格相等的最长子序列长度。

转移十分显然,线段树维护即可。时间复杂度 O(n log n)。

co int N=200000+10;
int a[N],b[N];

int f[N*4],g[N*4],h[N*4];
#define lc (x<<1)
#define rc (x<<1|1)

void chgf(int x,int l,int r,int p,int v){
    if(l==r){
        f[x]=max(f[x],v);
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid) chgf(lc,l,mid,p,v);
    else chgf(rc,mid+1,r,p,v);
    f[x]=max(f[lc],f[rc]);
}
void chgg(int x,int l,int r,int p,int v){
    if(l==r){
        g[x]=max(g[x],v);
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid) chgg(lc,l,mid,p,v);
    else chgg(rc,mid+1,r,p,v);
    g[x]=max(g[lc],g[rc]);
}
void chgh(int x,int l,int r,int p,int v){
    if(l==r){
        h[x]=max(h[x],v);
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid) chgh(lc,l,mid,p,v);
    else chgh(rc,mid+1,r,p,v);
    h[x]=max(h[lc],h[rc]);
}

int qryf(int x,int l,int r,int ql,int qr){
    if(ql<=l and r<=qr) return f[x];
    int mid=(l+r)>>1;
    if(qr<=mid) return qryf(lc,l,mid,ql,qr);
    if(ql>mid) return qryf(rc,mid+1,r,ql,qr);
    return max(qryf(lc,l,mid,ql,qr),qryf(rc,mid+1,r,ql,qr));
}
int qryg(int x,int l,int r,int ql,int qr){
    if(ql<=l and r<=qr) return g[x];
    int mid=(l+r)>>1;
    if(qr<=mid) return qryg(lc,l,mid,ql,qr);
    if(ql>mid) return qryg(rc,mid+1,r,ql,qr);
    return max(qryg(lc,l,mid,ql,qr),qryg(rc,mid+1,r,ql,qr));
}
int qryh(int x,int l,int r,int ql,int qr){
    if(ql<=l and r<=qr) return h[x];
    int mid=(l+r)>>1;
    if(qr<=mid) return qryh(lc,l,mid,ql,qr);
    if(ql>mid) return qryh(rc,mid+1,r,ql,qr);
    return max(qryh(lc,l,mid,ql,qr),qryh(rc,mid+1,r,ql,qr));
}

int main(){
    freopen("decoration.in","r",stdin),freopen("decoration.out","w",stdout);
    int n=read<int>();
    for(int i=1;i<=n;++i) b[i]=read(a[i]);
    sort(b+1,b+n+1);
    int m=unique(b+1,b+n+1)-b-1;
    
    for(int i=1;i<=n;++i){
//      cerr<<"calc "<<i<<endl;
        a[i]=lower_bound(b+1,b+m+1,a[i])-b;
        
        int tmpf=a[i]>1?max(qryf(1,1,m,1,a[i]-1),qryh(1,1,m,1,a[i]-1)):0;
        int tmpg=a[i]<m?max(qryg(1,1,m,a[i]+1,m),qryh(1,1,m,a[i]+1,m)):0;
        int tmph=max(qryf(1,1,m,a[i],a[i]),max(qryg(1,1,m,a[i],a[i]),qryh(1,1,m,a[i],a[i])));
//      cerr<<" f="<<tmpf<<" g="<<tmpg<<" h="<<tmph<<endl;
        
        chgf(1,1,m,a[i],tmpf+1);
        chgg(1,1,m,a[i],tmpg+1);
        chgh(1,1,m,a[i],tmph+1);
    }
    int ans=max(qryf(1,1,m,1,m),max(qryg(1,1,m,1,m),qryh(1,1,m,1,m)));
//  cerr<<"ans="<<ans<<endl;
    printf("%d\n",n-ans);
    return 0;
}

大样例是错的导致我调了一个半小时,后来写了个对拍才逐渐怀疑std出锅。

最短路径

Mope 做了一个梦。

在梦中,Mope 在一座沼泽地里。沼泽地里有 n 个村落,由 m 座木桥连接并连通。他发现自己的手臂上出现了一个数字,这个数字一开始是 0。每一座木桥上也有一个数字 wi。当 Mope 经过第 i 座桥时,他手臂上的数字会变成和 wi做按位或的结果。Mope 现在在 1 号村落,村落里的长老告诉他在 n 号村落里有一个魔法宝箱,他手臂上的数字越大,宝箱里的财宝就越少。Mope 希望知道当他到达 n 号村落时,手臂上的数字最小能是多少。

对于所有测试点:1 ≤ n ≤ 100,000, n − 1 ≤ m ≤ min(n(n−1)/2 , 200,000), 0 ≤wi < 230

题解

按位贪心,并查集判连通性即可。时间复杂度 O(n log w)。

扫描二维码关注公众号,回复: 7377953 查看本文章
co int N=100000+10;
struct edge {int u,v,w;}e[2*N];
int fa[N];
int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}

int main(){
    freopen("path.in","r",stdin),freopen("path.out","w",stdout);
    int n=read<int>(),m=read<int>();
    for(int i=1;i<=m;++i) read(e[i].u),read(e[i].v),read(e[i].w);
    int ans=0;
    for(int c=29;c>=0;--c){
        for(int i=1;i<=n;++i) fa[i]=i;
        for(int i=1;i<=m;++i)if((e[i].w>>c|ans>>c)==ans>>c){
            int fu=find(e[i].u),fv=find(e[i].v);
            if(fu!=fv) fa[fu]=fv;
        }
        if(find(1)!=find(n)) ans^=1<<c;
    }
    printf("%d\n",ans);
    return 0;
}

繁衍

2077 年,生物研究者们发现了一种神奇的生物——Mebius。

Mebius 是一种单细胞生物,生殖方式为分裂生殖。目前研究者们已发现了Mebius 的 n 个亚种,分别标号为 1~n。奇特的是,某个亚种的 Mebius 可能分裂成其他亚种的 Mebius。

Mope 是一名生物研究员,他发现 1 类亚种 Mebius 潜藏着巨大的医学价值。为了一探究竟,他需要大量的 1 类亚种 Mebius。他现在拥有一些不同种类的Mebius,他拥有大量的某种分裂促进因子可以使 Mebius 立即分裂。我们可以认为 Mope 拥有几乎无限量的分裂促进因子。Mope 想知道他最多能得到多少 1 类Mebius。

对于所有测试点:1 ≤ n ≤ 100,000, ∑ki ≤ 500,000, 0 ≤ ci ≤ 109

题解

先去掉到不了 1 的点,然后就考虑一下环的情况就行了。

容易发现若 ci>0,那么只能存在一个包含 1 的简单环。若有 ci=0,那么情况要复杂些,用 Tarjan 将 SCC 缩点后原图变成 DAG,那么对于一个环如果能到它的点和它自己的点的点权和是 0 的话它也是合法的。

这些都可以用 Tarjan 和拓扑排序判断。时间复杂度 O(n+m)。

co int N=100000+10;
int cnt[N];
vector<int> to[N],re[N];
int vis[N];

vector<int> scc[N];
int bl[N],tot;
int pos[N],low[N],dfn,st[N],top,ins[N];

void tarjan(int x,int fa){
    pos[x]=low[x]=++dfn;
    st[++top]=x,ins[x]=1;
    for(int i=0;i<(int)re[x].size();++i){
        int y=re[x][i];
        if(!pos[y]){
            tarjan(y,x);
            low[x]=min(low[x],low[y]);
        }
        else if(ins[y]) low[x]=min(low[x],pos[y]);
    }
    if(low[x]==pos[x]){
        ++tot;
        do{
            int y=st[top];
            ins[y]=0;
            bl[y]=tot,scc[tot].push_back(y);
        }while(st[top--]!=x);
    }
}

LL scnt[N];
vector<int> sto[N];
int svis[N];

void build(int x){
    svis[x]=1;
    for(int i=0;i<(int)sto[x].size();++i){
        int y=sto[x][i];
        if(svis[y]){
            scnt[x]+=scnt[y]; // edit 1
            continue;
        }
        build(y);
        scnt[x]+=scnt[y];
    }
    if(scnt[x])for(int i=0;i<(int)scc[x].size();++i) vis[scc[x][i]]=1;
}

int deg[N];
deque<int> Q;

int cir[N],len;
void dfs(int x){
    vis[x]=2,cir[++len]=x;
    for(int i=0;i<(int)to[x].size();++i){
        int y=to[x][i];
        if(vis[y]==1) dfs(y);
    }
}
int main(){
    freopen("multiplication.in","r",stdin),freopen("multiplication.out","w",stdout);
    read<int>();
    int n=read<int>();
    for(int x=1;x<=n;++x){
        for(int k=read<int>();k--;){
            int y=read<int>();
            to[x].push_back(y),re[y].push_back(x);
        }
    }
    for(int i=1;i<=n;++i) read(cnt[i]); 
    
    tarjan(1,0);
    
    for(int i=1;i<=tot;++i)
        for(int j=0;j<(int)scc[i].size();++j){
            int x=scc[i][j];
            scnt[i]+=cnt[x];
            for(int k=0;k<(int)re[x].size();++k){
                int y=re[x][k];
                if(i!=bl[y]) sto[i].push_back(bl[y]);
            }
        }
    build(bl[1]);
    
    for(int x=1;x<=n;++x)if(vis[x])
        for(int i=0;i<(int)to[x].size();++i){
            int y=to[x][i];
            if(vis[y]) ++deg[y];
        }
    for(int x=1;x<=n;++x)if(vis[x] and !deg[x]) Q.push_back(x);
    while(Q.size()){
        int x=Q.front();
        vis[x]=2,Q.pop_front();
        for(int i=0;i<(int)to[x].size();++i){
            int y=to[x][i];
            if(vis[y]){
                cnt[y]=add(cnt[y],cnt[x]);
                if(--deg[y]==0) Q.push_back(y);
            }
        }
    }
    
    int valid_deg=1,cir_siz=0;
    for(int i=1;i<=n;++i)if(vis[i]==1){
        if(deg[i]>1) {valid_deg=0;break;}
        ++cir_siz;
    }
    if(cir_siz) dfs(1);
    if(!valid_deg or cir_siz!=len) {puts("Infinity");return 0;}
    
    int ans=0;
    if(cir_siz)for(int i=1;i<=len;++i) ans=add(ans,cnt[cir[i]]);
    else ans=cnt[1];
    printf("%d\n",ans);
    return 0;
}

这玩意真不适合断断续续写代码,一个小错调一下午。

猜你喜欢

转载自www.cnblogs.com/autoint/p/test20190927.html
z