图论(?专项测试1

T1 序列

开题感觉用普通数据结构维护有(gen)点(ben)麻(bu)烦(hui),于是想了想,可以分块做。

在每个块内将元素从小到大排序,那么取 \(max\) 的操作会影响的只会是每个块左部的一个区间,可以打个差分标记维护修改次数。

做加法的操作比较好说,每个块上打完标记后,接下来的取 \(max\) 直接按这个标记的值偏移一下,就不用把加法标记真正打下去。

具体地,先给 \(a_i=0\)\(2\) ,再让它与 \(5\)\(max\) ,就可以看做 \(a_i=0\)\(5-2=3\)\(max\) ,最后查询时再加 \(2\)

所以加法可以相当于无视掉了。每次做零散操作时把 \(max\) 和差分标记下放就好。

粗略算了下,块长好像 \(\frac{\sqrt n}{2}\) 最优,但实测 \(\frac{\sqrt n}{3}\) 更快。( waitingcoders 上只能 \(\frac{\sqrt n}{3}\) 才能过

\(code:\)

T1
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    typedef long long LL;
    #define int LL
    int read(){
        int x=0,f=0; char ch=getchar();
        while(ch>'9'||ch<'0'){ f|=(ch=='-'); ch=getchar(); }
        while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return f?-x:x;
    } char output[50];
    void write(LL x,char sp){
        int len=0;
        if(x<0) putchar('-'), x=-x;
        do{ output[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
    }
} using namespace IO;

const int NN=100010;
int n,m,a[NN];
char op;

namespace Blocks{
    const int BB=800;
    struct node{
        LL vl; int id;
        node(){}
        node(int t){ vl=t; }
        bool operator<(const node& t)const{
            return vl<t.vl;
        }
    }s[NN];
    int blo,lp[BB],rp[BB],bel[NN],pos[NN];
    LL tag[BB],gat[BB],dve[NN],mnv[BB],slf[NN];
    void init(){
        blo=sqrt(n)/2;
        for(int i=1;i<=n;i++){
            s[i].vl=a[i]; s[i].id=i;
            bel[i]=(i-1)/blo+1;
            if(!lp[bel[i]]) lp[bel[i]]=i;
            if(!rp[bel[i]-1]) rp[bel[i]-1]=i-1;
        }
        rp[bel[n]]=n;
        for(int i=1;i<=bel[n];i++){
            sort(s+lp[i],s+rp[i]+1);
            mnv[i]=s[lp[i]].vl;
        }
        for(int i=1;i<=n;i++) pos[s[i].id]=i;
    }
    void update(int id){
        for(int sum=0,i=lp[id];i<=rp[id];i++){
            s[i].vl=max(s[i].vl,mnv[id]);
            sum+=dve[i]; dve[i]=0;
            slf[s[i].id]+=sum;
        }
    }
    void add(int l,int r,int v){
        if(!v) return;
        update(bel[l]); update(bel[r]);

        for(int i=l;i<=min(r,rp[bel[l]]);i++) s[pos[i]].vl+=v, ++slf[i];
        sort(s+lp[bel[l]],s+rp[bel[l]]+1); mnv[bel[l]]=s[lp[bel[l]]].vl;
        for(int i=lp[bel[l]];i<=rp[bel[l]];i++) pos[s[i].id]=i;

        if(bel[l]==bel[r]) return;

        for(int i=lp[bel[r]];i<=r;i++) s[pos[i]].vl+=v, ++slf[i];
        sort(s+lp[bel[r]],s+rp[bel[r]]+1); mnv[bel[r]]=s[lp[bel[r]]].vl;
        for(int i=lp[bel[r]];i<=rp[bel[r]];i++) pos[s[i].id]=i;

        for(int i=bel[l]+1;i<bel[r];i++)
            tag[i]+=v, ++gat[i];
    }
    void mxn(int l,int r,int v){
        update(bel[l]); update(bel[r]);

        for(int i=l;i<=min(r,rp[bel[l]]);i++)
            if(s[pos[i]].vl<v-tag[bel[l]]) s[pos[i]].vl=v-tag[bel[l]], ++slf[i];
        sort(s+lp[bel[l]],s+rp[bel[l]]+1); mnv[bel[l]]=s[lp[bel[l]]].vl;
        for(int i=lp[bel[l]];i<=rp[bel[l]];i++) pos[s[i].id]=i;

        if(bel[l]==bel[r]) return;

        for(int i=lp[bel[r]];i<=r;i++)
            if(s[pos[i]].vl<v-tag[bel[r]]) s[pos[i]].vl=v-tag[bel[r]], ++slf[i];
        sort(s+lp[bel[r]],s+rp[bel[r]]+1); mnv[bel[r]]=s[lp[bel[r]]].vl;
        for(int i=lp[bel[r]];i<=rp[bel[r]];i++) pos[s[i].id]=i;

        for(int i=bel[l]+1;i<bel[r];i++) if(mnv[i]<v-tag[i]){
            int loc=lower_bound(s+lp[i],s+rp[i]+1,node(v-tag[i]))-s;
            ++dve[lp[i]]; mnv[i]=v-tag[i];
            if(loc<=rp[i]) --dve[loc];
        }
    }
    void query(int k){
        update(bel[k]);
        write(s[pos[k]].vl+tag[bel[k]],' ');
        write(slf[k]+gat[bel[k]],'\n');
    }
    void solve(){
        while(m--){
            cin>>op;
            if(op=='A'){
                int l=read(),r=read(),c=read();
                add(l,r,c);
            } else if(op=='M'){
                int l=read(),r=read(),c=read();
                mxn(l,r,c);
            } else query(read());
        }
    }
}

signed main(){
//	freopen("seq.in","r",stdin);
//	freopen("seq.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    Blocks::init();
    m=read();
    Blocks::solve();
    return 0;
}

T2 旅行计划

以下默认 \(u,v\) 联通。

\(k\) 为奇数时,在 \(u,v\) 之间走 \(k\) 遍就能取到 \(0\) 。接下来考虑偶数 \(k\)

\(E\)\(u,v\) 所在联通块的边集,设 \(d=gcd(k,gcd_{l\in E}\left\{w_l\right\})\) ,那么答案肯定为 \(d\) 的倍数。不妨暂时将所有 \(w_l\) 都除以 \(d\) ,最终计算时再乘回去。

此时 \(gcd(k,gcd_{l\in E}\left\{w_l\right\})=1\) ,根据裴蜀定理,存在集合 \(A\) ,使 \(\sum_{l\in E}A_lw_l\equiv 1(\mod k)\) 。对于一个联通图,我们可以通过遍历 \(DFS\) 树,遇到非树边时往返的方式构造一个每条边都出现两次的回路。所以不难调整出贡献为 \(2\) 的环。因此实际上答案只与奇偶有关。

\(d'=gcd_{l\in E}\left\{w_l\right\}\) ,那么当 \(\frac{d}{d'}\) 为偶数时,所有 \(\frac{w_l}{k}\) 都为偶数,因此肯定能调整出 \(0\) 。否则 \(\frac{w_l}{d}\)\(\frac{w_l}{d'}\) 奇偶性相同,因此可以直接将边权预处理为 \(\frac{w_l}{d'}\) ,检查 \(u,v\) 所在联通块内是否存在奇环,或 \(u,v\) 间是否有长为偶数的路径。若有奇环,则可以通过奇环调整奇偶性,从而得到 \(0\) ;若有长为偶数的路径答案也应为 \(0\) ,否则就只能为 \(1\) ,即最终答案为 \(d\)

检查偶数路径可以直接在 \(DFS\) 树上染两种色,然后看两个点颜色有没有交集。

\(code:\)

T2
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    #define x first
    #define y second
    #define gcd __gcd
    int read(){
        int x=0,f=0; char ch=getchar();
        while(ch>'9'||ch<'0'){ f|=(ch=='-'); ch=getchar(); }
        while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return f?-x:x;
    } char output[50];
    void write(int x,char sp){
        int len=0;
        if(x<0) putchar('-'), x=-x;
        do{ output[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
    }
} using namespace IO;

const int NN=100010;
int n,m,q,dep[NN],col[NN];
vector<pair<int,int>>e[NN];

namespace DSU{
    int ff[NN],gg[NN];
    bool odd[NN];
    int getf(int x){ return x==ff[x]?x:ff[x]=getf(ff[x]); }
    void merge(int x,int y,int w){
        x=getf(x); y=getf(y);
        if(x==y) return;
        if(gg[x]) gg[x]=gcd(gg[x],w);
        else gg[x]=w;
        if(gg[y]) gg[x]=gcd(gg[x],gg[y]);
        ff[y]=x;
    }
} using namespace DSU;

void dfs(int u,int d){
    dep[u]=d;
    for(auto& v:e[u]){
        if(dep[v.x]>=0){
            if(dep[u]-dep[v.x]+v.y&1) odd[getf(u)]=1;
            continue;
        }
        dfs(v.x,d+v.y);
    }
}
void Dfs(int u,int c){
    if(col[u]&(1<<c)) return;
    col[u]|=(1<<c);
    for(auto& v:e[u]) Dfs(v.x,c^v.y);
}

signed main(){
//	freopen("travel.in","r",stdin);
//	freopen("travel.out","w",stdout);
    n=read(); m=read(); q=read();
    for(int i=1;i<=n;i++) ff[i]=i;
    for(int u,v,w,i=1;i<=m;i++){
        u=read(),v=read(),w=read(),merge(u,v,w);
        e[u].push_back({v,w}); e[v].push_back({u,w});
    }
    for(int i=1;i<=n;i++)
        for(auto& v:e[i])
            v.y=v.y/gg[getf(i)]&1;
    memset(dep,0xc0,sizeof(dep));
    for(int i=1;i<=n;i++) if(dep[i]<0)
        dfs(i,0), Dfs(i,0);
    while(q--){
        int u=read(),v=read(),k=read(),g=gcd(k,gg[getf(u)]);
        if(getf(u)!=getf(v)){ puts("NIE"); continue; }
        if(odd[getf(u)]||(col[u]&col[v])||!(gg[getf(u)]/g&1)){ puts("0"); continue; }
        write(g,'\n');
    }
    return 0;
}

T3 hack

如果不考虑恰好一次的限制,就是求最小割。考虑怎么满足限制。

发现不满足限制的情况肯定是一些不相交路径之间存在横叉边。要使这个横叉边前后不同时存在割边,可以对每个边连一条流量为 \(\infty\) 的反边。

这样连边后要注意 \(S\) 不能到达的点或不能到达 \(T\) 的点不能连边,不然会改变原图的联通性而导致错误,如 \(0\to 2,2\to 1,1\to 0\)\(0\to 2\) 本来是割边,连反边后却不再是了。

无解当且仅当 \(S\)\(T\) 在一个强连通分量内。

\(code:\)

T3
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    #define int long long
    int read(){
        int x=0,f=0; char ch=getchar();
        while(ch>'9'||ch<'0'){ f|=(ch=='-'); ch=getchar(); }
        while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return f?-x:x;
    } char output[50];
    void write(int x,char sp){
        int len=0;
        if(x<0) putchar('-'), x=-x;
        do{ output[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
    }
} using namespace IO;

const int NN=110,MM=200010;
int n,m,uu[MM],vv[MM],ww[MM];

namespace Network_Flows{
    int S,T,ql,qr,cut,q[NN],idx=1;
    int c[MM],to[MM],nex[MM],dis[NN],thd[NN],head[NN];
    void add(int a,int b,int x){
        to[++idx]=b; nex[idx]=head[a]; head[a]=idx; c[idx]=x;
        to[++idx]=a; nex[idx]=head[b]; head[b]=idx; c[idx]=0;
    }
    bool bfs(){
        memset(dis,0x3f,sizeof(dis));
        memcpy(head,thd,sizeof(thd));
        dis[S]=0; q[ql=qr=1]=S;
        while(ql<=qr){
            int u=q[ql++];
            for(int v,i=head[u];i;i=nex[i]) if(c[i])
                if(dis[v=to[i]]>dis[u]+1){
                    dis[v]=dis[u]+1;
                    q[++qr]=v;
                }
            if(u==T) return 1;
        }
        return 0;
    }
    int dfs(int u,int in){
        if(u==T) return in;
        int rest=in,go;
        for(int v,i=head[u];i;head[u]=i=nex[i]) if(c[i]){
            if(dis[v=to[i]]==dis[u]+1){
                go=dfs(v,min(rest,c[i]));
                if(go) c[i]-=go, c[i^1]+=go, rest-=go;
                else dis[v]=0;
            }
            if(!rest) break;
        }
        return in-rest;
    }
    void dinic(){
        memcpy(thd,head,sizeof(head));
        while(bfs()) cut+=dfs(S,LLONG_MAX);
    }
} using namespace Network_Flows;

vector<int>e[NN],E[NN];
int top,cnt,tmp,num,scc[NN],stk[NN],low[NN],dfn[NN];
bool vis[NN];
void tarjan(int u){
    vis[stk[++top]=u]=1; low[u]=dfn[u]=++cnt;
    for(int v:e[u])
        if(!dfn[v]) tarjan(v), low[u]=min(low[u],low[v]);
        else if(vis[v]) low[u]=min(low[u],dfn[v]);
    if(low[u]==dfn[u]){
        ++num;
        do{ scc[tmp=stk[top--]]=num; vis[tmp]=0; }while(tmp!=u);
    }
}

int col[NN];
void Dfs(int u,int t){
    if(vis[u]) return;
    vis[u]=1; col[u]|=t;
    if(t==1) for(int v:e[u]) Dfs(v,t);
    else for(int v:E[u]) Dfs(v,t);
}


signed main(){
    n=read(); m=read(); T=n-1;
    for(int w,i=1;i<=m;i++){
        uu[i]=read(); vv[i]=read(); ww[i]=read();
        e[uu[i]].push_back(vv[i]);
        E[vv[i]].push_back(uu[i]);
    }
    tarjan(0);
    Dfs(S,1); memset(vis,0,sizeof(vis)); Dfs(T,2);
    if(scc[0]==scc[n-1]) return puts("-1"),0;
    for(int i=1;i<=m;i++) if(col[uu[i]]==3&&col[vv[i]]==3)
        add(uu[i],vv[i],ww[i]), add(vv[i],uu[i],LLONG_MAX);
    dinic();
    write(cut,'\n');
    return 0;
}
posted @ 2021-12-23 16:38  keen_z  阅读(39)  评论(0编辑  收藏  举报