noip模拟17
阅读原文时间:2023年07月09日阅读:1

\(\color{white}{\mathbb{霞光划破暗淡天际,月影彷徨,鸡鸣仿佛,冀之以继往开来,名之以:黎明}}\)

今天似乎取得了有史以来最好的成绩~

前两名都 A 掉了 \(t3\),然鹅 \(t3\) 上去就被我弃了……

前两天部分分都写得比较顺利

\(t1\) 一眼以为 \(bitset\) 裸题,开了 \(6e4 * 6e4\) 直接自信提交,后来一算才发现空间炸了

但是不记得一位 \(bitset\) 是多大,以为和 \(bool\) 一样,只能开 \(10^4 * 10^4\),没敢打另外 20% 的部分分,暴挂20分

\(t2\) 网络流板子忘得差不多了,口胡了还几次才过了样例


A. 世界线

读懂题很快就能看出来是 \(bitset\),但是空间比较卡

首先,\(bitset\) 是每8位占用一个字节,一次运算复杂度为 \(n/32\)

那么其实前 \(60%\) 都是可以轻松水过去的

发现最后两个点的理论空间都比能开下的 \(bitset\) 大一倍,那么可以采用把所有点分成两部分的方式统计,\(bitset\) 只开一半,第一次统计所有点能到达的 \(\le n/2\) 的点的个数,再来一次统计能到达的 \(>n/2\) 的点的个数,直接相加即可

代码实现

#include<bits/stdc++.h>
using namespace std;
const int maxn=6e4+5;
const int maxm=1e6+5;
int n,m,deg[maxn],x,y,hd[maxn],cnt,limit,out[maxn];
bitset<maxn/2>vis[maxn];
long long ans;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        x=x*10+ch-48;
        ch=getchar();
    }
    return x*f;
}
struct Edge{
    int nxt,to;
}edge[maxm];
void add(int u,int v){
    edge[++cnt].nxt=hd[u];
    edge[cnt].to=v;
    hd[u]=cnt;
    return ;
}
void topsort(int op){
    queue<int>q;
    for(int i=1;i<=n;i++){
        if(!deg[i])q.push(i);
    }
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=hd[u];i;i=edge[i].nxt){
            int v=edge[i].to;
            vis[v]|=vis[u];
            if(!(--deg[v])){
                q.push(v);
            }
        }
    }
    return ;
}
int main(){
    n=read();
    m=read();
    for(int i=1;i<=m;i++){
        x=read();
        y=read();
        deg[x]++;
        add(y,x);
    }
    for(int i=1;i<=n;i++){
        ans-=deg[i]+1;
        out[i]=deg[i];
    }
    limit=n/2;
    for(int i=1;i<=limit;i++){
        vis[i][i]=1;
    }
    topsort(0);
    for(int i=1;i<=n;i++){
        ans+=vis[i].count();
//        cout<<vis[i].count()<<endl;
        vis[i].reset();
        deg[i]=out[i];
    }
//    vis.reset();
    for(int i=limit+1;i<=n;i++){
        vis[i][i-limit]=1;
    }
    topsort(1);
    for(int i=1;i<=n;i++){
//        cout<<vis[i].count()<<" ";
        ans+=vis[i].count();
    }
    cout<<ans;
    return 0;
}

/*
5 5
1 2
1 3
2 3
3 4
4 5
*/ 

B. 时间机器

说实话考场上没有认真看懂题,光用网络流水了一下居然拿到了70分

这道题正解贪心

考虑所有元件和节点按左端点第一关键字右端点第二关键字排序后,那么右端点靠左的元件越容易用不上,所以尽可能优先选取右端点靠左的元件

那么对于一个节点拥有一个左右端点满足条件的候选集合,考虑左端点对于后面的节点都是可用的,但是右端点可能会被淘汰掉,那么就选择右端点最靠左的一个元件进行匹配

这个可以用 \(set\) 实现

注意这道题一开始一直被卡常,后来发现 \(s.lower\_bound(xxx)\) 比 \(lower\_bound(s.begin(),s.end(),xxx)\) 快好多

代码实现

#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int maxn=1e6+5;
int t,n,m,tp;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        x=x*10+ch-48;
        ch=getchar();
    }
    return x*f;
}
struct Th{
    int l,r,num;
}a[maxn],b[maxn];
struct Node{
    int fi;
    mutable int se;
    Node(int x=0,int y=0):fi(x),se(y){}
};
bool operator < (const Node &x,const Node &y){
    return x.fi!=y.fi?x.fi<y.fi:x.se<y.se;
}
multiset<Node>s;
bool operator < (const Th &x,const Th &y){
    return x.l==y.l?x.r<y.r:x.l<y.l;
}
void solve(){
    tp=1;
    for(int i=1;i<=n;i++){
        while(tp<=m&&b[tp].l<=a[i].l){
            s.insert(Node(b[tp].r,b[tp].num));
            tp++;
        }
        while(a[i].num){
            multiset<Node>::iterator it=s.lower_bound(Node(a[i].r,0));
            if(it==s.end()){
                puts("No");
                return ;
            }
            if(a[i].num < it->se){
                it->se -= a[i].num;
                break;
            }
            a[i].num-= it->se;
            s.erase(it);
        }
    }
    puts("Yes");
}
signed main(){
    t=read();
    while(t--){
        n=read();
        m=read();
        for(int i=1;i<=n;i++){
            a[i].l=read();
            a[i].r=read();
            a[i].num=read();
        }
        for(int i=1;i<=m;i++){
            b[i].l=read();
            b[i].r=read();
            b[i].num=read();
        }
        sort(a+1,a+n+1);
        sort(b+1,b+m+1);
        s.clear();
        solve();
    }
    return 0;
}

/*
3
2 2
1 2 2
1 2 1
1 2 1
1 2 2
2 2
1 4 2
3 5 1
1 4 2
2 5 1

3 2
1 3 1
2 4 1
3 5 1
1 3 2
2 5 1

*/ 

C. weight

考完试 cyh 说和题单上的 Mst 这道题基本上是一样的,真后悔当时没看这道题

首先把最小生成树建出来,然后树边和非树边分别讨论

如果是非树边,那么答案为生成树上两端点路径上边权最大值 -1

如果是树边,则是所有和这条边有关的非树边的最小值 -1

可以采用树剖,每条非树边查询一次修改一次即可

cyh 的LCT理论复杂度少一个 \(log\),也非常好维护

注意判不在连通块里的边(可以用并查集判断),树剖需要边化点,更新时不能选上面的那个点

于是一直调一直改写了将近300行……

代码实现

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
const int maxm=4e5+5;
//const int inf=
int n,m,ans[maxn],x,y,w,op;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        x=x*10+ch-48;
        ch=getchar();
    }
    return x*f;
}
namespace p2{
    int hd[maxn],cnt,cnt1,re[maxn],fa[maxn],tot,id[maxn],pa[maxn],siz[maxn],son[maxn],dep[maxn],tp[maxn],val[maxn];
    struct Edge{
        int nxt,to,val,id;
    }edge[maxm];
    void add(int u,int v,int w,int num){
        edge[++cnt].nxt=hd[u];
        edge[cnt].to=v;
        edge[cnt].val=w;
        edge[cnt].id=num;
        hd[u]=cnt;
        return ;
    }
    struct Ch{
        int from,to,id,val;
    }link[maxm];
    void make(int u,int v,int w,int num){
        link[++cnt1].to=v;
        link[cnt1].from=u;
        link[cnt1].val=w;
        link[cnt1].id=num;
        return ;
    }
    void dfs(int u){
//        cout<<u<<" "<<endl;
        siz[u]=1;
        for(int i=hd[u];i;i=edge[i].nxt){
            int v=edge[i].to;
            if(v==fa[u])continue;
            dep[v]=dep[u]+1;
            fa[v]=u;
            val[v]=edge[i].val;
            pa[v]=edge[i].id;
//            cout<<"hhh "<<v<<" "<<pa[v]<<endl;
            dfs(v);
            siz[u]+=siz[v];
            if(siz[v]>siz[son[u]])son[u]=v;
        }
        return ;
    }
    void dfs1(int u,int top){
        tp[u]=top;
        id[u]=++tot;
        re[tot]=u;
//        cout<<u<<" ";
        if(son[u])dfs1(son[u],top);
        for(int i=hd[u];i;i=edge[i].nxt){
            int v=edge[i].to;
            if(v==fa[u]||v==son[u])continue;
            dfs1(v,v);
        }
        return ;
    }
    struct Seg{
        int l,r,mx,lazy;
    }t[maxn*4];
    void update(int p){
        t[p].mx=max(t[p<<1].mx,t[p<<1|1].mx);
        return ;
    }
    void build(int p,int l,int r){
        t[p].l=l;
        t[p].r=r;
        t[p].lazy=0x3f3f3f3f;
        if(l==r){
            t[p].mx=val[re[l]];
//            cout<<"kkk "<<l<<" "<<val[id[l]]<<endl;
            return ;
        }
        int mid=l+r>>1;
        build(p<<1,l,mid);
        build(p<<1|1,mid+1,r);
        update(p);
        return ;
    }
    int ask(int p,int l,int r){
        if(l>r)return 0;
        if(t[p].l>=l&&t[p].r<=r)return t[p].mx;
        int mx=0;
        int mid=t[p].l+t[p].r>>1;
        if(l<=mid)mx=ask(p<<1,l,r);
        if(r>mid)mx=max(mx,ask(p<<1|1,l,r));
        return mx;
    }
    int ques(int x,int y){
        int mx=0;
//        cout<<"hhh "<<endl;
        while(tp[x]!=tp[y]){
            if(dep[tp[x]]<dep[tp[y]])swap(x,y);
//            cout<<"ppp "<<id[tp[x]]<<" "<<id[x]<<endl;
            mx=max(mx,ask(1,id[tp[x]],id[x]));
            x=fa[tp[x]];
        }
//        cout<<mx<<endl;
        if(dep[x]>dep[y])swap(x,y);
//        cout<<"ppp "<<id[x]+1<<" "<<id[y]<<endl;
        mx=max(mx,ask(1,id[x]+1,id[y]));
//        cout<<id[x]<<" "<<id[y]<<endl;
        return mx;
    }
    void ch(int p,int l,int r,int w){
        if(l>r)return ;
        if(t[p].l>=l&&t[p].r<=r){
            t[p].lazy=min(t[p].lazy,w);
            return ;
        }
        int mid=t[p].l+t[p].r>>1;
        if(l<=mid)ch(p<<1,l,r,w);
        if(r>mid)ch(p<<1|1,l,r,w);
        return ;
    }
    void change(int x,int y,int w){

        while(tp[x]!=tp[y]){
            if(dep[tp[x]]<dep[tp[y]])swap(x,y);

            ch(1,id[tp[x]],id[x],w);
            x=fa[tp[x]];
        }
//        cout<<mx<<endl;
        if(dep[x]>dep[y])swap(x,y);

        ch(1,id[x]+1,id[y],w);
        return ;
    }
    void spread(int p){
        if(t[p].l==t[p].r){
//            cout<<t[p].l<<" "<<re[t[p].l]<<" "<<pa[re[t[p].l]]<<endl;
            ans[pa[re[t[p].l]]]=t[p].lazy;
            return ;
        }
        t[p<<1].lazy=min(t[p<<1].lazy,t[p].lazy);
        t[p<<1|1].lazy=min(t[p<<1|1].lazy,t[p].lazy);
        spread(p<<1);
        spread(p<<1|1);
        return ;
    }
    void start(){
        dfs(1);
        pa[1]=m+1;
        dfs1(1,1);
        build(1,1,n);
//        for(int i=1;i<=n;i++){
//            cout<<pa[i]<<endl;
//        }
//        cout<<endl;
//        cout<<"hhh ";
        for(int i=1;i<=cnt1;i++){
            int x=link[i].from;
            int y=link[i].to;
//            cout<<link[i].id<<" "<<x<<" "<<y<<" "<<link[i].val<<endl;
            ans[link[i].id]=ques(x,y);
//            flag[link[i].id];
//            cout<<ans[link[i].id]<<endl;
//            cout<<link[i].id<<" "<<ans[link[i].id]<<endl;
            change(x,y,link[i].val);
        }
        spread(1);
        return ;
    }
}
namespace p1{
    int hd[maxn],cnt,fa[maxn];
    struct Edge{
        int nxt,to,from,val,id;
    }edge[maxm],edge1[maxm];
    void add(int u,int v,int w){
        edge[++cnt].nxt=hd[u];
        edge[cnt].to=v;
        edge[cnt].from=u;
        edge[cnt].val=w;
        return ;
    }
    bool cmp(Edge a,Edge b){
        return a.val<b.val;
    }
    int find(int x){
        return fa[x]==x?x:fa[x]=find(fa[x]);
    }
    void kruscal(){
        sort(edge+1,edge+m+1,cmp);
        for(int i=1;i<=n;i++){
            fa[i]=i;
        }
        for(int i=1;i<=m;i++){
            int u=edge[i].from;
            int v=edge[i].to;
            x=find(u);
            y=find(v);
            if(x!=y){
                fa[x]=y;
//                cout<<u<<" "<<v<<" "<<edge[i].val<<" "<<edge[i].id<<endl;
                p2::add(u,v,edge[i].val,edge[i].id);
                p2::add(v,u,edge[i].val,edge[i].id);
            }
            else{
//                cout<<u<<" "<<v<<" "<<edge[i].val<<" "<<edge[i].id<<endl;
                p2::make(u,v,edge[i].val,edge[i].id);
            }
        }
        return ;
    }
}
namespace p3{
    int fa[maxn],tot,tot1,idx[maxn];
    int find(int x){
        return fa[x]==x?x:fa[x]=find(fa[x]);
    }
    void pd(){
        for(int i=1;i<=n;i++){
            fa[i]=i;
        }
        for(int i=1;i<=m;i++){
            int x=p1::edge[i].from;
            int y=p1::edge[i].to;
            x=find(x);
            y=find(y);
            if(x!=y)fa[x]=y;
        }
        for(int i=1;i<=m;i++){
            p1::edge1[i]=p1::edge[i];
//            cout<<find(p1::edge[i].from)<<" "<<find(1)<<endl;
            if(find(p1::edge[i].from)!=find(1)&&find(p1::edge[i].to)!=find(1)){
                ans[p1::edge[i].id]=1;
//                cout<<"ppp "<<p1::edge[i].id<<endl;
                tot++;
            }
            else idx[++tot1]=i;
        }
        p1::cnt=0;
        memset(p1::hd,0,sizeof p1::hd);
        for(int i=1;i<=tot1;i++){
            p1::add(p1::edge1[idx[i]].from,p1::edge1[idx[i]].to,p1::edge1[idx[i]].val);
            p1::edge[p1::cnt].id=p1::edge1[idx[i]].id;
        }
        m=tot1;
    }
}
int main(){
//    freopen("c.in","r",stdin);
//    freopen("c.out","w",stdout);
    n=read();
    m=read();
    op=read();
    for(int i=1;i<=m;i++){
        x=read();
        y=read();
        w=read();
        p1::add(x,y,w);
        p1::edge[p1::cnt].id=i;
    }
//    cout<<"hhh ";
//    p3::tot1=m;
    p3::pd();
//    cout<<"ppp "<<m<<endl;
//    cout<<"hhh ";
//    for(int i=1;i<=m;i++){
//        cout<<p1::edge[i].from<<" "<<p1::edge[i].to<<" "<<p1::edge[i].val<<" "<<p1::edge[i].id<<endl;
//    }
    p1::kruscal();
//    cout<<"hhh ";
    p2::start();
    for(int i=1;i<=p3::tot+p3::tot1;i++){
        if(ans[i]==0x3f3f3f3f)printf("-1 ");
        else printf("%d ",ans[i]-1);
    }
    return 0;
}

/*
5 4 0
4 5 2
1 2 1
2 3 2
3 1 3

*/ 

\(\color{white}{\mathbb{困于心,衡于虑,而后作}}\)