PWJ的图论模板整理

图论

最大流

dinic

bool bfs(){
    for(int i=sb;i<=se;i++) dep[i]=0;
    dep[sb]=1;
    queue<int> q;
    q.push(sb);
    int u,v;
    while(!q.empty()){
        u=q.front();
        q.pop();
        for(int i=head[u];~i;i=S[i].ne){
            v=S[i].v;
            if(S[i].w>0&&!dep[v]){
                dep[v]=dep[u]+1;
                if(v==se) return true;
                q.push(v);
            }
        }
    }
    return false;
}
ll dfs(int u,ll minf){
    if(u==se||!minf) return minf;
    ll flow;
    int v;
    for(int &i=cur[u];~i;i=S[i].ne){
        v=S[i].v;
        if(S[i].w>0&&dep[v]==dep[u]+1){
            flow=dfs(v,min(S[i].w,minf));
            if(flow){
                S[i].w-=flow;
                S[i^1].w+=flow;
                return flow;
            }
        }
    }
    return 0;
}
ll dinic(){
    ll maxf=0,flow;
    while(bfs()){
        for(int i=sb;i<=se;i++) cur[i]=head[i];
        while(flow=dfs(sb,inf)) maxf+=flow;
    }
    return maxf;
}

isap

bool bfs(){
    for(int i=sb;i<=se;i++) dep[i]=0;
    dep[se]=1;
    queue<int> q;
    q.push(se);
    int u,v;
    while(!q.empty()){
        u=q.front();
        q.pop();
        for(int i=head[u];~i;i=S[i].ne){
            v=S[i].v;
            if(!dep[v]){
                dep[v]=dep[u]+1;
                q.push(v);
            }
        }
    }
    return dep[sb]!=0;
}
ll aug(){
    ll minf=inf; 
    for(int i=lu[se];~i;i=lu[S[i^1].v]) 
        minf=min(minf,S[i].w);
    for(int i=lu[se];~i;i=lu[S[i^1].v]){
        S[i].w-=minf;
        S[i^1].w+=minf;
    }
    return minf;
}
ll ispa(){
    ll maxf=0;
    bfs();
    for(int i=sb;i<=se;i++){
        lu[i]=-1;
        num[dep[i]]++;
        cur[i]=head[i];
    }
    int u=sb,v;
    while(dep[sb]<n){
        if(u==se){
            maxf+=aug();
            u=sb;
        }
        bool flag=false;
        for(int &i=cur[u];~i;i=S[i].ne){
            v=S[i].v;
            if(S[i].w>0&&dep[u]==dep[v]+1){
                flag=true;
                lu[v]=i;
                u=v;
                break;
            }
        }
        if(!flag){
            int mind=n-1;
            for(int i=head[u];~i;i=S[i].ne){
                v=S[i].v;
                if(S[i].w>0) mind=min(mind,dep[v]);
            }
            if(--num[dep[u]]==0) break;
            num[dep[u]=mind+1]++;
            cur[u]=head[u];
            if(u!=sb) u=S[lu[u]^1].v;
        }
    }
    return maxf;
}

最小费用最大流

bool spfa(int n)
{
    queue<int> q;
    for(int i=0;i<=n;i++)
    { 
        dis[i]=inf;
        vis[i]=0;
        flow[i]=inf;
        lu[i]=-1;
    }
    dis[s]=0;
    vis[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=S[i].ne)
        {
            int v=S[i].v;
            if(S[i].w>0&&dis[v]>dis[u]+S[i].val)
            {
                lu[v]=i;
                dis[v]=dis[u]+S[i].val;
                flow[v]=min(flow[u],S[i].w);
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    return dis[t]!=inf; 
}
void mfml(int n)
{
    int ans=0,ansc=0;
    while(spfa(n))
    {
        ans+=flow[t];
        ansc+=flow[t]*dis[t];
        for(int i=lu[t];i!=-1;i=lu[S[i^1].v])
        {
            S[i].w-=flow[t];
            S[i^1].w+=flow[t];
        }
    }
    printf("%d %d\n",ans,ansc);
}

zwk费用流

bool spfa(int n){
    for(int i=0;i<=n;i++){ 
        dis[i]=inf;
        vis[i]=false;
    }
    dis[se]=0;
    vis[se]=1;
    deque<int> q;
    q.push_back(se);
    while(!q.empty()){
        int u=q.front();
        q.pop_front();
        vis[u]=false;
        for(int i=head[u],v;~i;i=S[i].ne){
            v=S[i].v;
            if(S[i^1].w>0&&dis[v]>dis[u]-S[i].val){
                dis[v]=dis[u]-S[i].val;
                if(!vis[v]){
                    vis[v]=true;
                    if(!q.empty()&&dis[v]<dis[q.front()])
                        q.push_front(v);
                    else q.push_back(v);
                }
            }
        }
    }
    return dis[sb]!=inf; 
}
int dfs(int u,int minf){
    if(u==se){
        vis[se]=true;
        return minf;
    }
    vis[u]=true;
    int flow=0,temp,v;
    for(int i=head[u];~i;i=S[i].ne){
        v=S[i].v;
        if(!vis[v]&&S[i].w>0&&dis[v]==dis[u]-S[i].val){
            temp=dfs(v,min(S[i].w,minf-flow));
            if(temp){
                S[i].w-=temp;
                S[i^1].w+=temp;
                flow+=temp;
                ansc+=S[i].val*temp; 
            }
            if(flow==minf) break;
        }
    }
    return flow;
}
void mfml(int n){
    int ans=0;
    ansc=0;
    while(spfa(n)){
        vis[se]=true;
        while(vis[se]){
            for(int i=0;i<=n;i++) vis[i]=false;
            ans+=dfs(sb,inf);
        }
    }
    printf("%d %d\n",ans,ansc);
}

欧拉回路

#include<cstdio>
const int N=101108,M=201108;
struct Side{
    int v,ne,ok,id;
}S[M<<1];
int t,n,m,sn,cnt,head[N],cur[N],in[N],out[N],fa[N],ans[M<<1];
void init(){
    sn=0;
    for(int i=1;i<=n;i++){
        fa[i]=i;
        head[i]=-1;
        in[i]=out[i]=0;
    }
}
void add(int u,int v,int id){
    S[sn].ok=1;
    S[sn].id=id;
    S[sn].v=v;
    S[sn].ne=head[u];
    head[u]=sn++;
}
int find(int x){
    return fa[x]==x ? x : fa[x]=find(fa[x]); 
}
void bing(int x,int y){
    int fx=find(x),fy=find(y);
    if(fx!=fy) fa[fx]=fy;
    return ;
}
void dfs(int u){
    for(int &i=cur[u];~i;i=S[i].ne){
        if(S[i].ok){
            S[i].ok=0;
            int temp=i;
            if(t==1) S[i^1].ok=0;
            dfs(S[i].v);
            ans[cnt++]=S[temp].id;
            if(i==-1) break;
        }
    }
}
int main(){
    int u,v;
    while(~scanf("%d",&t)){
        scanf("%d%d",&n,&m);
        init();
        for(int i=1;i<=m;i++){
            scanf("%d%d",&u,&v);
            bing(u,v);
            if(t==1){
                in[u]++;
                in[v]++;
                add(u,v,i);
                add(v,u,-i);
            }else{
                in[v]++;
                out[u]++;
                add(u,v,i);
            }
        }
        int beg=1,flag=1,num=0;
        for(int i=1;i<=n;i++){
            cur[i]=head[i];
            if(fa[i]==i&&in[i]) num++;
            if(t==1){
                if(in[i]&1) flag=0;
                else if(in[i]) beg=i;
            }else{
                if(in[i]!=out[i]) flag=0;
                else if(in[i]) beg=i;
            }
            if(!flag||num>1) break;
        }
        if(flag&&num<=1){
            printf("YES\n");
            if(num){
                cnt=0;
                dfs(beg);
                for(int i=cnt-1;i>=0;i--) printf("%d%c",ans[i]," \n"[i==0]);
            }
        }else printf("NO\n");
    }
    return 0;
}

混合图欧拉回路

然后对于每个出度大于入度的点,我们把它与源点连一条流量为(出度-入度)/2的边,(出度-入度)/2其实就是需要改变多少条连到它上面的无向边,使得它出度等于入度。而对于入度大于出度的点,则是与汇点连一条流量为(入度-出度)/2的边。

所以最终能不能有欧拉回路,也就是看能不能满流,让每个点都满足出度等于入度的要求。

而欧拉路径的话,就是最多只能有两个出度与入度奇偶性不同的点,也就是终点和起点,然后我们找到这两个点,给它们连一条无向边,那处理就跟欧拉回路的一样了。

至于路径的输出,我们就看之前流量为1的那些边,如果流量变为0了,说明反向了,否则就是我们原定的方向,就用这些新的有向边和原来的有向边,然后有向图的欧拉回路就ok了

强连通分量

void tarjan(int u){
    ins[u]=true;
    sta[++tn]=u;
    dfn[u]=low[u]=++dn;
    for(int i=head[u],v;~i;i=S[i].ne){
        v=S[i].v;
        if(!dfn[v]){
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }else if(ins[v]) low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        col[u]=++cn;
        ins[u]=false;
        size[cn]=1;
        minc[cn]=u;
        while(sta[tn]!=u){
            col[sta[tn]]=cn;
            size[cn]++;
            minc[cn]=min(minc[cn],sta[tn]);
            ins[sta[tn--]]=false;
        }
        if(size[cn]>size[ansc]||(size[cn]==size[ansc]&&minc[cn]<minc[ansc])) ansc=cn;
        tn--;
    }
}

割点

对于一个割点来说,它割掉之后产生的连通块就是能让它是割点的点个数,因为那些点就代表着它那个连通块。

void findc(int u,int fa){
    dfn[u]=low[u]=++dn;
    int v,vvs=vv[u].size(),son=0;
    for(int i=0;i<vvs;i++){
        v=vv[u][i];
        if(!dfn[v]){
            if(u==fa) son++;
            findc(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]&&u!=fa) isc[u]=true;
        }else low[u]=min(low[u],dfn[v]);
    }
    if(u==fa&&son>=2) isc[u]=true;
    if(isc[u]) ans++;
}

割边

void findq(int u){
    dfn[u]=low[u]=++dn;
    int v,id;
    for(int i=head[u];~i;i=S[i].ne){
        v=S[i].v;
        id=S[i].id;
        if(!dfn[v]){
            fp[v]=id;
            findq(v);
            low[u]=min(low[u],low[v]);
        }else if(id!=fp[u]) low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]&&fp[u]!=-1){
        if(ans==-1) ans=val[fp[u]];
        else ans=min(ans,val[fp[u]]);
    }
}

点双连通分量

void pdc(int u,int fa){
    dfn[u]=low[u]=++dn;
    int v,son=0;
    for(int i=head[u];~i;i=S[i].ne){
        v=S[i].v;
        if(!dfn[v]){
            if(u==fa) son++;
            sta.push(Side(u,v));
            pdc(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]){
                if(u!=fa) isc[u]=true;
                bf[++bn]=u;
                while(!sta.empty()){
                    int su=sta.top().u,sv=sta.top().v;
                    sta.pop();
                    if(bin[su]!=bn) bin[su]=bn;
                    if(bin[sv]!=bn) bin[sv]=bn;
                    if(su==u&&sv==v) break;
                }
            }
        }else if(v!=fa){
            low[u]=min(low[u],dfn[v]);
            if(dfn[u]>dfn[v]) sta.push(Side(u,v));
        }
    }
    if(u==fa&&son>=2) isc[u]=true;
}

边双连通分量

void dfs(int u){
    bin[u]=bn;
    vis[u]=true;
    for(int i=head[u];~i;i=S[i].ne){
        if(isq[S[i].id]) continue;
        if(!vis[S[i].v]) dfs(S[i].v);
    }
}

个有桥的连通图,如何把它通过加边变成边双连通图:统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf。则至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,所以至少添加的边数就是(leaf+1)/2。具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。

一般图最大匹配

#include<cstdio>
#include<queue>
using namespace std;
const int N=511,M=2e5+11;
struct Side{
    int v,ne;
}S[M<<1];
queue<int> q;
int n,sn,fn,head[N],pp[N],col[N],pre[N],fa[N],vis[N];
//pp[i] i节点匹配的节点,pp[i]-i就是匹配边
//col[i] i节点在当前交错树的染色,用于判断奇环 
//pre[i] 记录交错树中i的前一个节点 i-pre[i]就是非匹配边 
//fa[i] i节点当前交错树花的lca,用于缩点,和判断奇环 
//vis用来判断在找lca时,某个点是否走过了
void init(){
    sn=fn=0;
    for(int i=0;i<=n;i++){
        pp[i]=0;
        vis[i]=0;
        head[i]=-1;
    }
}
void add(int u,int v){
    S[sn].v=v;
    S[sn].ne=head[u];
    head[u]=sn++;
}
int find(int x){
    return fa[x]==x ? x : fa[x]=find(fa[x]);
}
int flca(int x,int y){
    ++fn;
    x=find(x),y=find(y);
    //当发现奇环时,这两个节点在交错树的深度是相同的。 
    //两个节点匹配边-非匹配边交替往前找,遇到已经走到过的点,那就是花的lca了
    while(vis[x]!=fn){
        vis[x]=fn;
        x=find(pre[pp[x]]);
        if(y){
            int temp=x;x=y;y=temp;
        }
    }
    return x;
}
void blossom(int x,int y,int fl){
    //因为开花时奇环可以双向走,因此pre边也要变成双向的。 
    while(find(x)!=fl){
        pre[x]=y;//x是原本的黑点,y是原本的白点,原先已经有pre[y]=x 
        y=pp[x];
        if(col[y]==2){//原来是白的,变成黑的了,继续增广 
            col[y]=1;
            q.push(y);
        }
        //因为有些点可能已经缩在某朵花里了,所以只对还映射自己的点缩点 
        if(find(x)==x) fa[x]=fl;
        if(find(y)==y) fa[y]=fl;
        x=pre[y];//和上面的y=pp[x]就实现匹配边-非匹配边交替走 
    }
}
int aug(int u){
    for(int i=0;i<=n;i++){
        fa[i]=i;
        pre[i]=0;
        col[i]=0;
    }//fa pre col都是相对当前的交错树而言,所以每次都要清空 
    while(!q.empty()) q.pop();
    q.push(u);
    col[u]=1;
    while(!q.empty()){
        u=q.front();
        q.pop();
        for(int i=head[u],v;~i;i=S[i].ne){
            v=S[i].v;
            if(find(u)==find(v)||col[v]==2) continue;
            if(!col[v]){
                pre[v]=u;col[v]=2;
                if(!pp[v]){
                    for(int x=v,y;x;x=y){    
                        y=pp[pre[x]];
                        pp[x]=pre[x];
                        pp[pre[x]]=x;
                    }//非匹配边-匹配边,返回去修改 
                    return 1; 
                }
                col[pp[v]]=1;q.push(pp[v]);
            }else{//发现奇环,进行开花(缩点) 
                int fl=flca(u,v);
                blossom(u,v,fl);
                blossom(v,u,fl);
            }
        }
    }
    return 0;
}
int main(){
    int m,u,v;
    while(~scanf("%d%d",&n,&m)){
        init();
        while(m--){
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        int ans=0;
        for(int i=1;i<=n;i++) if(!pp[i]) ans+=aug(i);
        printf("%d\n",ans);
        for(int i=1;i<=n;i++) printf("%d%c",pp[i]," \n"[i==n]);
    }
    return 0;
}

一般图最大团

const int N=111;
bool mp[N][N];
int n,ans,mcq[N],vis[N],ids[N];
bool dfs(int *adj,int tot,int num){
    if(tot==0){
        if(num>ans){
            ans=num;
            for(int i=0;i<num;i++) ids[i]=vis[i];
            return true;
        }
        return false;
    }
    int temp[N],cnt;
    for(int i=0;i<tot;i++){
        if(num+tot-i<=ans) return false;
        if(num+mcq[adj[i]]<=ans) return false;
        cnt=0;
        for(int j=i+1;j<tot;j++) if(mp[adj[i]][adj[j]]) temp[cnt++]=adj[j];
        vis[num]=adj[i];
        if(dfs(temp,cnt,num+1)) return true; 
    }
    return false;
}
int maxcq(){
    int adj[N],cnt;
    ans=0;
    for(int i=n;i>=1;i--){
        cnt=0;
        vis[0]=i;
        for(int j=i+1;j<=n;j++) if(mp[i][j]) adj[cnt++]=j;
        dfs(adj,cnt,1);
        mcq[i]=ans;
    }
    return ans;
}

HK优化匈牙利

bool searchp() {
    queue<int>q;
    dis=INF;
    CLR(dx,-1);//memset
    CLR(dy,-1);
    for(int i=1;i<=n;i++) {
        if(mx[i]==-1) {
            q.push(i);
            dx[i]=0;
        }
    }
    while(!q.empty()) {
        int u=q.front();
        q.pop();
        if(dx[u]>dis) break;
        for(int i=head[u];~i;i=e[i].next) {
            int v=e[i].v;
            if(dy[v]==-1) {
                dy[v]=dx[u]+1;
                if(my[v]==-1) dis=dy[v];
                else {
                    dx[my[v]]=dy[v]+1;
                    q.push(my[v]);
                }
            }
        }
    }
    return dis!=INF;
}
bool dfs(int u) {
    for(int i=head[u];~i;i=e[i].next) {
        int v=e[i].v;
        if(vis[v]||(dy[v]!=dx[u]+1)) continue;
        vis[v]=1;
        if(my[v]!=-1&&dy[v]==dis) continue;
        if(my[v]==-1||dfs(my[v])) {
            my[v]=u;
            mx[u]=v;
            return true;
        }
    }
    return false;
}
int maxMatch() {
    int res = 0;
    CLR(mx,-1);
    CLR(my,-1);
    while(searchp()) {
        CLR(vis,0);
        for(int i=1;i<=n; i++)
            if(mx[i] == -1 && dfs(i))
                res++;
    }
    return res;
}

二分图最大权匹配KM

//bfsn3
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=411;
const ll inf=1e18+7;
queue<int> q;
ll ex[N],ey[N],del[N],val[N][N];
int nx,ny,vn,px[N],py[N],pre[N],visx[N],visy[N];
void init(){
    vn=0;
    for(int i=0;i<=ny;i++) py[i]=visy[i]=0;
    for(int i=0;i<=nx;i++) px[i]=visx[i]=0;
    for(int i=0;i<=ny;i++)
        for(int j=0;j<=nx;j++)
            val[i][j]=0;
}
bool aug(){
    int u,v;
    ll temp;
    while(!q.empty()){
        u=q.front();
        q.pop();
        for(int i=1;i<=nx;i++){
            if(visx[i]==vn) continue;
            temp=ey[u]+ex[i]-val[u][i];
            if(!temp){
                pre[i]=u;
                if(!px[i]){
                    for(int x=u,y=i,z;y;x=pre[y]){
                        z=py[x];
                        py[x]=y;
                        px[y]=x;
                        y=z;
                    }//跟带花树的类似,一样是匹配边非匹配边交替修改 
                    return 1;
                }
                visx[i]=vn;
                if(visy[px[i]]!=vn){
                    visy[px[i]]=vn;
                    q.push(px[i]); 
                }
            }else if(temp<del[i]){
                pre[i]=u;
                del[i]=temp;
            }
        }
    }
    return false;
}
void km(){
    for(int i=1;i<=ny;i++){
        ey[i]=0;
        for(int j=1;j<=nx;j++) ey[i]=max(ey[i],val[i][j]);
    }
    for(int i=1;i<=nx;i++) ex[i]=0;
    for(int i=1;i<=ny;i++){
        for(int j=1;j<=nx;j++){
            del[j]=inf;
            pre[j]=0;
        }
        while(!q.empty()) q.pop();
        q.push(i);
        visy[i]=++vn; 
        while(true){
            if(aug()) break;
            ll d=inf;
            for(int j=1;j<=nx;j++) if(visx[j]!=vn) d=min(d,del[j]);
            for(int j=1;j<=ny;j++) if(visy[j]==vn) ey[j]-=d;
            for(int j=1;j<=nx;j++) if(visx[j]==vn) ex[j]+=d;
            else if(del[j]<inf) del[j]-=d;
            //上面的跟dfs的处理一样 
            bool flag=false;
            for(int j=1;j<=nx;j++){//在调整了期望值后 
            //找到能参与进匹配的男生 
                if(visx[j]==vn||del[j]!=0) continue;
                if(!px[j]){
                    for(int x=pre[j],y=j,z;y;x=pre[y]){
                        z=py[x];
                        py[x]=y;
                        px[y]=x;
                        y=z;
                    }
                    flag=true;
                    break;
                }
                visx[j]=vn;
                if(visy[px[j]]!=vn){
                    visy[px[j]]=vn;
                    q.push(px[j]); 
                }
            }
            if(flag) break;
        }
    }
}
void solve(int n){
    km();
    ll ans=0;
    for(int i=1;i<=n;i++) ans+=val[px[i]][i];
    printf("%lld\n",ans);
    for(int i=1;i<=n;i++){
        if(!val[px[i]][i]) px[i]=0;
        printf("%d%c",px[i]," \n"[i==n]);
    }
}
int main(){
    ll w;
    int m,u,v,n;
    while(~scanf("%d%d%d",&nx,&ny,&m)){
        n=nx;
        if(nx<ny) nx=ny;
        init();
        while(m--){
            scanf("%d%d%lld",&v,&u,&w);
            val[u][v]=w;
        }
        solve(n);
    }
    return 0;
}

最小点覆盖:二分图中,选取最少的点数,使这些点和所有的边都有关联(把所有的边的覆盖),叫做最小点覆盖。

解决方法:最小点覆盖数 = 最大匹配数

最小路径覆盖:这跟一般图的是同一个。给定有向图G=(V,E)。设P 是G 的一个简单路(顶点不相交)的集合。如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖。P 中路径可以从V 的任何一个顶点开始,长度也是任意的,特别地,可以为0。G 的最小路径覆盖是G 的所含路径条数最少的路径覆盖。

解决方法:对于G中每一个节点x,建立节点x1,x2。若x- >y存在边,则x1与y2之间连一条无向边,求这个二分图的最大匹配数即可。然后,最小路径覆盖=|G|-最大匹配数

最大独立集:独立集是指图G中两两互不相邻的顶点构成的集合。最大,就是点数最多。

解决方法:最大独立集=总数-最小覆盖集

三元环

#include<cstdio>
#include<algorithm> 
#include<cstring> 
using namespace std;
typedef long long ll;
const int N=100118;
struct Side{
    int v,ne;
}S[2*N];
int sn,head[N],link[N],line[N],du[N],val[2*N],u[2*N],v[2*N];
inline void init(int n)
{
    sn=0;
    for(int i=0;i<=n;i++)
    {
        head[i]=-1;
        du[i]=0;
        link[i]=0;
        line[i]=-1;
    }
}
inline void add(int u,int v)
{
    S[sn].v=v;
    S[sn].ne=head[u];
    head[u]=sn++;
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        init(n);
        for(int i=0;i<m;i++)
        {
            val[i]=0;
            scanf("%d%d",&u[i],&v[i]);
            du[u[i]]++,du[v[i]]++;
        }
        for(int i=0;i<m;i++)
        {
            if(du[u[i]]<du[v[i]])
                add(u[i],v[i]);
            else if(du[u[i]]>du[v[i]])
                add(v[i],u[i]);
            else 
            {
                if(u[i]<v[i])
                    add(u[i],v[i]);
                else
                    add(v[i],u[i]);
            }
        }
        for(int i=0;i<m;i++)
        {
            int x=u[i],y=v[i];
            for(int j=head[x];j!=-1;j=S[j].ne)
            {
                link[S[j].v]=x;
                line[S[j].v]=j;
            }
            for(int j=head[y];j!=-1;j=S[j].ne)
            {
                int z=S[j].v;
                if(link[z]==x)
                {
                    val[i]++;
                    val[j]++;
                    val[line[z]]++;    
                }
            }
        }
        ll ans=0;
        for(int i=0;i<m;i++)
            ans+=1ll*val[i]*(val[i]-1)/2;
        printf("%lld\n",ans);
    }
    return 0;
}

CDQ分治陌上花开

#include<cstdio>
#include<algorithm>
#define lowb(x) (x&(-x))
using namespace std;
const int N=100118,K=200118;
struct Flower{
    int id,s,c,m,w;
    friend bool operator == (const Flower &f1,const Flower &f2){
        return f1.s==f2.s&&f1.m==f2.m&&f1.c==f2.c;
    }
}F[N],temp[N];
int fn,ans[N]={0},rank[N]={0},summ[K]={0};
//ans[i]保存编号为i的花的等级,也就是属性小于或等于它的数目 
bool cmp(const Flower &f1,const Flower &f2){
    return f1.s==f2.s ? (f1.c==f2.c ? f1.m<f2.m : f1.c<f2.c) : f1.s<f2.s;
}
void updata(int m,int val)
{
    while(m<=200000)
    {
        summ[m]+=val;
        m+=lowb(m);
    }
}
int getsum(int m)
{
    int ans=0;
    while(m)
    {
        ans+=summ[m];
        m-=lowb(m);
    }
    return ans;
}
void clear(int m)
{
    while(m<=200000)
    {
        if(summ[m]) 
            summ[m]=0;
        else
            break;
        m+=lowb(m);
    }
}
void cdq(int l,int r)
{
    if(l==r)
    {
        ans[F[l].id]+=F[l].w-1;//因为相同属性的归到一类了
        //所以还得加上F[l].w-1,也就是除它之外,
        //其他相同属性的花的数目 
        return ;
    }
    int mid=(l+r)>>1;
    cdq(l,mid);
    cdq(mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r)
    {
        if(F[i].c<=F[j].c)
        {
            updata(F[i].m,F[i].w);
            temp[k++]=F[i++];
        }
        else
        {
            ans[F[j].id]+=getsum(F[j].m);
            temp[k++]=F[j++];
        }
    }
    while(i<=mid)
        temp[k++]=F[i++];
    while(j<=r)
    {
        ans[F[j].id]+=getsum(F[j].m);
        temp[k++]=F[j++];
    }
    for(i=l;i<=r;i++)
    {
        clear(F[i].m);
        F[i]=temp[i];   
    }
}
int main()
{
    int n,k;
    while(~scanf("%d%d",&n,&k))
    {
        for(int i=0;i<n;i++)
        {
            F[i].w=1;
            scanf("%d%d%d",&F[i].s,&F[i].c,&F[i].m);
        }
        sort(F,F+n,cmp);
        fn=0;
        //去重 
        for(int i=0;i<n;i++)
        {
            if(i&&F[i]==F[i-1])//如果和前一朵花属性相同,归到一类 
                F[fn-1].w++;
            else
            {
                F[fn]=F[i];
                F[fn].id=fn++;
            }
        }
        //原先n朵花去重就只有fn朵 
        cdq(0,fn-1);
        for(int i=0;i<fn;i++)
        {
            rank[ans[F[i].id]]+=F[i].w;
            ans[F[i].id]=0;
        }
        for(int i=0;i<n;i++)
        {
            printf("%d\n",rank[i]);
            rank[i]=0;
        }
    }
    return 0;
} 

biset5维偏序

#include<cstdio>
#include<bitset>
using namespace std;
const int N=30118;
int a[N][6],b[6][N];
//a[i][j]第i个人第j门功课排第几, b[i][j]第i功课排第j的是谁 
bitset<N> c[6][N],ans;//c[i][j]第i门功课排第j的人,名次比他高的有谁 
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++)
            for(int j=0;j<5;j++)
            {
                scanf("%d",&a[i][j]);
                a[i][j]--;//因为我是从0开始的,所以这里-- 
                b[j][a[i][j]]=i;
                c[j][i].reset();
            }
        for(int i=0;i<5;i++)
            for(int j=1;j<n;j++)
            {
                c[i][j]=c[i][j-1];
                c[i][j].set(b[i][j-1]);
            }
        for(int i=0;i<n;i++)
        {
            ans=c[0][a[i][0]];
            for(int j=1;j<5;j++)
                ans&=c[j][a[i][j]];
            printf("%d\n",ans.count());
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/LMCC1108/p/11548508.html
今日推荐