bzoj1924

转移自老blog

bzoj1924

题意:

        在宽广的非洲荒漠中,生活着一群勤劳勇敢的羊驼家族。被族人恭称为“先知”的Alpaca L. Sotomon是这个家族的领袖,外人也称其为“所驼门王”。所驼门王毕生致力于维护家族的安定与和谐,他曾亲自率军粉碎河蟹帝国主义的野蛮侵略,为族人立下赫赫战功。所驼门王一生财宝无数,但因其生性节俭低调,他将财宝埋藏在自己设计的地下宫殿里,这也是今天Henry Curtis故事的起点。Henry是一个爱财如命的贪婪家伙,而又非常聪明,他费尽心机谋划了这次盗窃行动,破解重重机关后来到这座地下宫殿前。
        整座宫殿呈矩阵状,由R×C间矩形宫室组成,其中有N间宫室里埋藏着宝藏,称作藏宝宫室。宫殿里外、相邻宫室间都由坚硬的实体墙阻隔,由一间宫室到达另一间只能通过所驼门王独创的移动方式——传送门。所驼门王为这N间藏宝宫室每间都架设了一扇传送门,没有宝藏的宫室不设传送门,所有的宫室传送门分为三种:
        “横天门”:由该门可以传送到同行的任一宫室;
        “纵寰门”:由该门可以传送到同列的任一宫室;
        “自*河蟹*由*河蟹*门”:由该门可以传送到以该门所在宫室为中心周围8格中任一宫室(如果目标宫室存在的话)。
        深谋远虑的Henry当然事先就搞到了所驼门王当年的宫殿招标册,书册上详细记录了每扇传送门所属宫室及类型。而且,虽然宫殿内外相隔,但他自行准备了一种便携式传送门,可将自己传送到殿内任意一间宫室开始寻宝,并在任意一间宫室结束后传送出宫。整座宫殿只许进出一次,且便携门无法进行宫室之间的传送。不过好在宫室内传送门的使用没有次数限制,每间宫室也可以多次出入。
        现在Henry已经打开了便携门,即将选择一间宫室进入。为得到尽多宝藏,他希望安排一条路线,使走过的不同藏宝宫室尽可能多。请你告诉Henry这条路线最多行经不同藏宝宫室的数目。

输入:
        第一行给出三个正整数 N, R, C。 以下 N 行,每行给出一扇传送门的信息,包含三个正整数xi, yi, Ti,表示该传送门设在位于第 xi行第yi列的藏宝宫室,类型为 Ti。Ti是一个1~3间的整数, 1表示可以传送到第 xi行任意一列的“横天门”,2表示可以传送到任意一行第 yi列的“纵寰门”,3表示可以传送到周围 8格宫室的“自由门”。 保证 1≤xi≤R,1≤yi≤C,所有的传送门位置互不相同。
测试点编号:
        
        N  R  C 
        1  16  20  20 
        2  300  1,000  1,000 
        3  500  100,000  100,000  
        4  2,500  5,000  5,000  
        5  50,000  5,000  5,000  
        6  50,000  1,000,000  1,000,000  
        7  80,000  1,000,000  1,000,000  
        8  100,000  1,000,000  1,000,000  
        9  100,000  1,000,000  1,000,000  
        10  100,000  1,000,000  1,000,000  
强连通缩点后跑spfa最长路即可
但是如何建图很重要,我们暴力建图,复杂度会达到n^2
但是发现可以这样做,对于横天门,每行找出一个横天门,与该行其他横天门连双向边,即表示了这一堆强联通,对于该行的其他门,连单向边,
对于纵寰门同理
对于河蟹门,暴力连边复杂度不会过高
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<set>
#include<map>

using namespace std;

struct Graph{
    static const int maxn=1e5+5,maxm=maxn*8+maxn*2+maxn*2+maxn*8;// 3号节点最多 n*8  ; 1/2号节点 最多双向边n*2  
    struct star{int v,w,nex;} edge[maxm];
    int head[maxn],cnt,n;
    void ini(int n){
        this->n=n; cnt=-1;
        for(int i=0;i<=n;i++) head[i]=-1;
    }
    void add_edge(int u,int v,int w){
        edge[++cnt]=star{v,w,head[u]}; head[u]=cnt;
    }
};

struct Tarjan:Graph{//强连通分量缩点
    int low[maxn],dfn[maxn],belong[maxn],stk[maxn],instk[maxn],block[maxn];
    int step,color;
    void tarjan(){
        step=color=0;
        for(int i=0;i<=n;i++) dfn[i]=0;
        for(int i=1;i<=n;i++) if(dfn[i]==0) tarjan(i,0);//多个联通快
    }
    void tarjan(int u,int father=0){//此函数不开放
        low[u]=dfn[u]=++step; stk[++stk[0]]=u;instk[u]=1;
        for(int i=head[u];~i;i=edge[i].nex){
            int v=edge[i].v;
            if(dfn[v]) {
                if(instk[v]) low[u]=min(low[u],dfn[v]);
            }
            else{
                tarjan(v,u);
                low[u]=min(low[u],low[v]);
            }
        }
        if(low[u]==dfn[u]){
            block[color+1]=1;
            while(stk[stk[0]]!=u) {
                belong[stk[stk[0]]]=color+1;
                instk[stk[stk[0]--]]=0;
                block[color+1]++;
            }
            belong[stk[stk[0]]]=++color;
            instk[stk[stk[0]--]]=0;
        }
    }
}graph;

struct Spfa:Graph{
    int d[maxn],inq[maxn];
    void short_path(int s,int*dist){
        for(int i=0;i<=n;i++) dist[i]=1e9;
        dist[s]=0;
        deque<int>q;
        q.push_back(s);
        inq[s]=1;
        long long sum=0;
        while(!q.empty()){
            int u=q.front(); q.pop_front(); sum-=dist[u];inq[u]=0;
            if(1ll*dist[u]*q.size()>sum){//large label last
                sum+=dist[u];
                q.push_back(u);
                inq[u]=1;
            }
            else{
                for(int i=head[u];~i;i=edge[i].nex){
                    int v=edge[i].v, w=edge[i].w;
                    if(dist[v]>dist[u]+w){
                        if(inq[v]){
                            sum-=dist[v];
                            dist[v]=dist[u]+w;
                            sum+=dist[v];
                        }
                        else{
                            dist[v]=dist[u]+w;
                            inq[v]=1;
                            sum+=dist[v];
                            if(dist[v]<dist[q.front()]) q.push_front(v);//small lable first
                            else q.push_back(v);
                        }
                    }
                }
            }
        }
    }
}g;

inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}

const int maxn=1e6+100;
vector<int>row[maxn],col[maxn];
vector<int>Row[maxn],Col[maxn];
struct node{int x,y;};
vector<node>block;

map<long long,int>mp;
int getid(int x,int y){
    static int cnt=0;
    long long t=x*1e6+y;
    if(mp[t]!=0) return mp[t];
    else return mp[t]=++cnt;
}
bool have(int x,int y){
    long long t=x*1e6+y;
    return mp.find(t)!=mp.end();
}

int bound[8][2]={-1,-1, -1,0, -1,1, 0,-1, 0,1, 1,-1, 1,0, 1,1};

int main(){
    int n=read();
    int r=read();
    int c=read();
    graph.ini(n);
    for(int i=0;i<n;i++){
        int u=read();
        int v=read();
        int t=read();
        getid(u,v);

        if(t==1) {
            col[v].push_back(u);
            Row[u].push_back(v);
        }
        else if(t==2) {
            row[u].push_back(v);
            Col[v].push_back(u);
        }
        else{
            row[u].push_back(v);
            col[v].push_back(u);
            block.push_back(node{u,v});
        }
    }

    //1
    for(int i=0;i<=r;i++){
        if(!Row[i].empty()){
            int quick=getid(i,Row[i][0]);
            for(int t=1;t<Row[i].size();t++){
                int quickkk=getid(i,Row[i][t]);
                graph.add_edge(quick,quickkk,1);
                graph.add_edge(quickkk,quick,1);
            }
            for(int t=0;t<row[i].size();t++){
                graph.add_edge(quick,getid(i,row[i][t]),1);
            }
        }
    }

    //2
    for(int i=0;i<=c;i++){
        if(!Col[i].empty()){
            int quick=getid(Col[i][0],i);
            for(int t=1;t<Col[i].size();t++){
                int quickkk=getid(Col[i][t],i);
                graph.add_edge(quick,quickkk,1);
                graph.add_edge(quickkk,quick,1);
            }
            for(int t=0;t<col[i].size();t++){
                graph.add_edge(quick,getid(col[i][t],i),1);
            }
        }
    }

    //3
    for(int i=0;i<block.size();i++){
        int u=block[i].x;
        int v=block[i].y;
        int quick=getid(u,v);
        for(int j=0;j<8;j++){
            int uu=u+bound[j][0];
            int vv=v+bound[j][1];
            if(have(uu,vv)){
                graph.add_edge(quick,getid(uu,vv),1);
            }
        }
    }

    graph.tarjan();

    g.ini(graph.color+1);

    set<long long>se;
    for(int u=1;u<=graph.n;u++){
        for(int i=graph.head[u];~i;i=graph.edge[i].nex){
            int v=graph.edge[i].v;
            int uu=graph.belong[u];
            int vv=graph.belong[v];
            if(uu==vv) continue;
            long long hash=uu*1e6+vv;
            if(se.find(hash)!=se.end()) continue;
            se.insert(hash);
            g.add_edge(uu,vv,-graph.block[vv]);
        }
    }

    int s=graph.color+1;
    for(int i=1;i<=graph.color;i++) g.add_edge(s,i,-graph.block[i]);
    g.short_path(s,g.d);


    int ans=0;
    for(int i=1;i<=graph.color;i++) ans=min(ans,g.d[i]);
    cout<<abs(ans)<<endl;

}

bzoj2118

转移自老blog

bzoj2118

Dedivion
墨墨突然对等式很感兴趣,他正在研究a1x1+a2y2+…+anxn=B存在非负整数解的条件,他要求你编写一个程序,给定N、{an}、以 及B的取值范围,求出有多少B可以使等式存在非负整数解。

Input
输入的第一行包含3个正整数,分别表示N、BMin、BMax分别表示数列的长度、B的下界、B的上界。输入的第二行包含N个整数,即 数列{an}的值。

Output
输出一个整数,表示有多少b可以使等式存在非负整数解。

Sample Input
2 5 10
3 5

Sample Output
5

HINT
对于100%的数据,N≤12,0≤ai≤5*10^5,1≤BMin≤BMax≤10^12。

f[i]表示模x意义下等于i时的最小背包容量
如果我们取x=min(ai),
则对于所有的f[i],f[i]+k*ai一定能够取得到
f用最短路来求即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main(){
    ll n,l,r;
    scanf("%lld%lld%lld",&n,&l,&r);
    ll mi=1<<30;
    ll a[12];
    for(ll i=0;i<n;i++) {
        scanf("%lld",a+i);
        mi=min(mi,a[i]);
    }
    vector<ll> dis(mi,1e18);
    priority_queue<ll,vector<ll>,greater<ll> > q;
    q.push(0);
    dis[0]=0;
    while(!q.empty()){
        ll u=q.top();q.pop();
        for(ll i=0;i<n;i++){
            ll v=u+a[i];
            if(dis[v%mi]>dis[u%mi]+a[i]) {
                dis[v%mi]=dis[u%mi]+a[i];
                q.push(v);
            }
        }
    }
    l--;
    long long ans=0;
    for(ll i=0;i<mi;i++){
        if(dis[i]<=l) ans-=(l-dis[i])/mi+1;
        if(dis[i]<=r) ans+=(r-dis[i])/mi+1;
    }
    cout<<ans<<endl;
}
// 5 6 8 9 10
// 2 0 2 1 1

bzoj2730

转移自老blog

bzoj2730

题意:
        煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。

输入:
        输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖 S 与挖煤点 T 由隧道直接连接。输入数据以 0 结尾。
对点双联通分支割点个数分类讨论,
分枝上共计一个点:忽略
没有割点:建立两个出口
一个割点:建立一个出口
其余:不建出口
#include<iostream>
#include<cstdio>

using namespace std;

struct Graph{
    static const int maxn=1e5+5, maxm=3e5+5;
    struct star{int v,nex;}edge[maxm<<1];
    int head[maxn],cnt,n;
    void ini(int n){
        this->n=n; cnt=-1;
        for(int i=0;i<=n;i++) head[i]=-1;
    }
    void add_edge(int u,int v){
        edge[++cnt]=star{v,head[u]}; head[u]=cnt;
        edge[++cnt]=star{u,head[v]}; head[v]=cnt;
    }
}tree;

struct Tarjan:Graph{//割点
    int low[maxn],dfn[maxn],stk[maxn],cut[maxn];
    int step;
    void tarjan(){
        step=0;
        for(int i=0;i<=n;i++) dfn[i]=cut[i]=0;
        for(int i=1;i<=n;i++) if(dfn[i]==0) tarjan(i,0);//多个联通快
    }
    void tarjan(int u,int father=0){//此函数不开放
        low[u]=dfn[u]=++step; stk[++stk[0]]=u;
        int first=1, son=0;
        for(int i=head[u];~i;i=edge[i].nex){
            int v=edge[i].v;
            if(v==father&&!first) first=false;
            else if(dfn[v]) low[u]=min(low[u],dfn[v]);
            else{
                son++;
                tarjan(v,u);
                low[u]=min(low[u],low[v]);
                //一个顶点u是割点,当且仅当1或2
                //1.u为树根且u有多与一个子树
                //2.u不为树根且存在边(u,v)为树边,使得dfn[u]<=low[v]
                if(father!=0&&dfn[u]<=low[v]) cut[u]=1;
                if(father==0&&son>1) cut[u]=1;
            }
        }
        stk[0]--;
    }
    long long nums,cutnums,ans1,ans2;
    void solve(int Case){
        tarjan();
        ans1=0,ans2=1;
        for(int i=1;i<=n;i++) dfn[i]=0;
        for(int i=1;i<=n;i++) {
            if(!cut[i]&&dfn[i]==0) {
                nums=cutnums=0;
                dfs(i,i);
                if(nums==1);//do nothing
                else{
                    if(cutnums>=2) ;//do nothing
                    else if(cutnums==1){
                        ans1++;
                        ans2*=nums-1;
                    }
                    else{
                        ans1+=2;
                        ans2*=(nums-1)*nums/2;
                    }
                }
            }
        }
        cout<<"Case "<<Case<<": "<<ans1<<" "<<ans2<<endl;//输出结果 
    }
    void dfs(int u,int color){
        nums++;
        cutnums+=cut[u];
        dfn[u]=color;
        if(cut[u]) return;
        for(int i=head[u];~i;i=edge[i].nex){
            int v=edge[i].v;
            if(dfn[v]!=color) dfs(v,color);
        }
    }
}graph;

inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}

int main(){
    int t=0;
    while(true){
        int m=read(),n=0;
        if(m==0) break;
        graph.ini(1000);
        for(int i=0;i<m;i++) {
            int u=read(),v=read();
            graph.add_edge(u,v);
            n=max(u,n);
            n=max(v,n);
        }
        graph.n=n;
        graph.solve(++t);

    }
}

hdu5727

转移自老blog

hdu5727

题意:
        给你最n个阳珠子和n个阴珠子(n≤9),要你串成阴阳相隔的珠链,有一些阳珠子不能和某些阴珠子放一起,否则会失去光芒,询问至少有几个阳珠子失去光芒.
因为阴阳相隔,所以我们可以考虑枚举阴珠子的全排列,在阴珠子之间插入阳珠子完成,插入使用二分图匹配完成。
优化:
  1.考虑旋转,锁定全排列中第一个数字即可
  2.考虑翻转,hash环排列以及他的翻转排列即可
#include<bits/stdc++.h>
using namespace std;

const double eps=1e-12;
struct dinic{
    static const int maxn=30;
    static const int maxm=900;
    static const int inf=1e9;
    struct edge{
        int v,nex;
        double c;
    } g[2*maxm];
    int lv[maxn],current[maxn],head[maxn],cnt;
    
    void add_edge(int u,int v,double c){
       //cout<<u<<" "<<v<<endl;
        g[cnt].v=v;
        g[cnt].c=c;
        g[cnt].nex=head[u];
        head[u]=cnt++;
        
        g[cnt].v=u;
        g[cnt].c=0;
        g[cnt].nex=head[v];
        head[v]=cnt++;
    }
    
    void ini(){
        memset(head,-1,sizeof(head));
        cnt=0;
    }
    
    void bfs(int s){
        memset(lv,-1,sizeof(lv));
        lv[s]=0;
        queue<int>q;
        q.push(s);
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=head[u]; ~i; i=g[i].nex){
                edge&e=g[i];
                //if(e.c<=0||lv[e.v]>=0)continue;
                if(e.c<2*eps||lv[e.v]>=0)continue;
                lv[e.v]=lv[u]+1;
                q.push(e.v);
            }
        }
    }
    
    double dfs(int u,int t,double f){
        if(u==t)return f;
        for(int&i=current[u]; ~i; i=g[i].nex){
            edge&e=g[i],&rev=g[i^1];
            //if(e.c<=0||lv[u]>=lv[e.v])continue;
            if(e.c<eps||lv[u]>=lv[e.v])continue;
            double d=dfs(e.v,t,min(f,e.c));
            //if(d<=0)continue;
            if(d<eps)continue;
            e.c -=d;
            rev.c+=d;
            return d;
        }
        return 0;
    }
    
    double maxflow(int s,int t){
        double flow=0;
        while(true){
            memmove(current,head,sizeof(head));
            bfs(s);
            if(lv[t]<0)return flow;
            double f;
            //while((f=dfs(s,t,inf))>0)flow+=f;
            while((f=dfs(s,t,inf))>eps)flow+=f;//注意括号
        }
    }
} g;


bool limit[20][20];
int a[20];

int gethash(int*a,int n){// [1,n]
    int ret=a[1];
    for(int i=2;i<=n;i++){
        ret*=10;
        ret+=a[i];
    }
    return ret;
}
int gethashrev(int*a,int n){// [1,n]
    int ret=a[1];
    for(int i=n;i>=2;i--){
        ret*=10;
        ret+=a[i];
    }
    return ret;
}

map<int,bool>mp;

int solve(int *a,int n){
    g.ini();
    int s=2*n+1;
    int t=2*n+2;
    
    for(int i=1;i<=n;i++){
        g.add_edge(s, i, 1);
        g.add_edge(i+n,t,1);
        
        for(int j=1;j<=n;j++){
            if((!limit[a[i]][j])&&(!limit[a[i+1]][j])){
                g.add_edge(i, j+n, 1);
            }
        }
    }
    
    return g.maxflow(s,t)+0.5;
}



int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        if(n==0){
            cout<<0<<endl;
            continue;
        }
        //init
        for(int i=1;i<=n;i++)a[i]=i;
        a[n+1]=1; //a[i]..a[i+1]   1<=i<=n
        memset(limit,0,sizeof(limit));
        mp.clear();
        
        while(m--){
            int u,v;
            scanf("%d%d",&u,&v);
            limit[v][u]=true;
        }
        
        int ans=1000;
        
        do{
            int hash=gethash(a,n);
            if(mp[hash]==true)continue;
            mp[hash]=true;
            mp[gethashrev(a,n)]=true;
            ans=min(ans,n-solve(a,n));
            
        }while(next_permutation(a+2,a+1+n));
        
        cout<<ans<<endl;
    }
}

hdu5729

转移自老blog

hdu5729

题意:
        给你n行m列的网格图,对于一个网格图,他是不稳定的,因为他是四边形,允许你在四边形里面加边斜边,斜边有两种,加斜边之后当前格子变成两个三角形具有稳定性,当所有格子稳定时,称整个网格稳定。询问你有多少种加边的方法使得网格图稳定。
一方面:
先考虑网格不稳定的表现,发现有一些当前垂直的横边与竖边,不具有稳定性,有变得不垂直的可能。
再考虑网格稳定的表现,如果所有的横边与竖边永远保持垂直,那么网格稳定。
-------(1)


另一个方面:
先考虑网格不稳定的表现,发现有一些当前平行的横边间或竖边间,不具有稳定性,有变得不平行的可能。
再考虑网格稳定的表现,如果所有的横边间永远平行且竖边间永远平行且存在一条横边与竖边垂直,那么网格稳定。
-------(2)


总结:
当所有的横边间、竖边间永远平行,所有的横边与竖边永远垂直时,网格稳定。
-------(3)


不知道什么定理:
如果线段a平行于线段b,线段a垂直于线段c,那么线段b垂直于线段c。
-------(4)

如果线段a垂直与线段c,线段b垂直与线段c,那么线段a平行于线段b。
-------(5)


观察发现:
对于列位置相同的横边集,永远平行,对于行位置相同的竖边集,永远平行。
-------(6)


对根据上诉现象及分析,对边按照分类,列位置相同的边属于相同的集合,行位置相同的边属于相同集合。

然后考虑加斜边的影响:
每加入一条斜边,使得当前格子稳定。表现为使得当前格子的横边与竖边永远垂直。由(4)和(6)我们得到: 若命名:当前格子所在横边所属与的集合为a,当前格子所在竖边所属于的集合为b。 则集合a中所有边与集合b中所有边垂直。

如果我们用图(不一定是二分图)来表示这些集合之间的关系,将所有横边集组成集合A,将所有竖边集组成集合B,用图的边来表示垂直或平行关系,若边的顶点在集合内部,则边代表平行,否则代表垂直。用由(4)和(5)得出此无向图具有传递性。

当此图的传递闭包为完全图时,由(3)得出网格稳定。此命题等价于:若原图联通,则网格稳定。

于是原问题等价于:
给你集合A有n个点,集合B有m个点,有两种权值的无向边,可以添加到属于不同集合的点之间,问你有多少种加边方法可以让此图联通,
这时候我们才发现,其实不允许在集合内部加边,也就是说,此图的原型实际上为二分图
于是原问题等价于:
给你一个二分图,包含两个集合,集合A有n个点,集合B有m个点,允许添加两种权值的无向边,问你有多少种加边方法可以让此二分图联通。

对于新的问题,我们采取dp解决:

dp[i][j]代表左边i个点右边j个点的答案;

正面难以解决,考虑反面;

i+j个点的此二分图一共有种,从左边拿一个点出来考虑;

对于此点,分类讨论 ,考虑与它联通的所有其他点的数量,设左边有x个右边有y个,剩下的其他点自由组合,

于是 x的范围[0,i-1] y的范围[0,j];
这些类别全部加起来应该是全集
于是得到

得到dp式子:
当x等于i-1时y不取到j



#include <bits/stdc++.h>
using namespace std;
#define ll long long

const ll mod = 1e9 + 7;
ll C[20][20], dp[20][20], pow3[200];

int main() {
    
    pow3[0] = 1;
    for (int i = 1; i < 200; i++) pow3[i] = pow3[i - 1] * 3 % mod;
    
    C[0][0] = 1;
    for (int i = 1; i < 20; i++) {
        C[i][0] = 1;
        for (int j = 1; j <= i; j++) {
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
        }
    }
    
    for (int i = 0; i <= 10; i++) {
        for (int j = 0; j <= 10; j++) {
            dp[i][j] = 1000*mod+pow3[i*j];
            for (int x = 0; x <= i - 1; x++) {
                for (int y = 0; y <= j; y++) {
                    if (x == i - 1 && y == j)continue;
                    dp[i][j] -= C[i - 1][x] * C[j][y] % mod*dp[x + 1][y] % mod*pow3[(i - 1 - x)*(j - y)] % mod;
                }
            }
            dp[i][j] %= mod;
        }
    }
    
    int n, m;
    while (~scanf("%d%d", &n, &m))printf("%lld\n", dp[n][m]);
    
}

hdu6445

转移自老blog

hdu6445

题意:
        Given a tournament, you need to determine the direction of the remaining sides to maximize the answer. The answer is calculated in the following way. The vertices are labeled from 0 to n−1, and the matrix s is used to represent the edges.
        给你一个竞赛图,你需要确定某些剩余的边的方向,来最大化答案,答案通过下面的算法得到,点从0到n-1,s[i][j]是边
        输入文件s[i][j]=1代表i->j,s[i][j]=0代表j->i,s[i][j]=2代表方向未知
        化简分析,先加一个A(n,4) 然后减去对答案没有贡献以及负贡献,考虑对答案没有贡献的四元组,此四元组中存在且只存在一个点有两条边 从他出发,对答案贡献-1的点,存在恰好两个点,分别有两条边从他们出发。
        化最后发现只和点的出度的平方或者说(C(deg[i],2)) 因为sum(deg[i])为定值有关
        化最后可以用费用流做
#include<bits/stdc++.h>
using namespace std;


struct MCMF{
    static const int maxn=200+2+200*200,maxm=maxn*5;
    struct star{int v,nex;int c,w;} edge[maxm<<1];
    int head[maxn],cnt,n;
    int inq[maxn],pre[maxn];
    int dist[maxn];

    void ini(int n){
        cnt=-1;this->n=n;
        for(int i=0;i<=n;i++) head[i]=-1;
    }
    void add_edge(int u, int v, int c, int w){
        // cout<<"     "<<u<<" "<<v<<" "<<c<<" "<<w<<endl;
        edge[++cnt]=star{v,head[u],c, w}; head[u]=cnt;
        edge[++cnt]=star{u,head[v],0,-w}; head[v]=cnt;
    }
    void minCostMaxFlow(int s, int t,int&flow,int&cost){
        flow=cost=0;
        while(true){
            for(int i=0;i<=n;i++) dist[i]=1e9;
            queue<int>que; que.push(s);
            inq[s]=1; dist[s]=0;

            while(!que.empty()){
                int u=que.front();
                que.pop(); inq[u]=0;
                for(int i=head[u];~i;i=edge[i].nex){
                    int v=edge[i].v;
                    int c=edge[i].c,w=edge[i].w;
                    // if(c>eps&&dist[v]>dist[u]+w+eps){
                    if(c>0&&dist[v]>dist[u]+w){
                        dist[v]=dist[u]+w;
                        pre[v]=i;
                        if(!inq[v]) que.push(v);
                        inq[v]=1;
                    }
                }
            }
            if(dist[t]==1e9) return ;
            int addf=1e9;
            for(int x=t;x!=s;x=edge[pre[x]^1].v) addf=min(addf,edge[pre[x]].c);
            for(int x=t;x!=s;x=edge[pre[x]^1].v){
                edge[pre[x]].c-=addf;
                edge[pre[x]^1].c+=addf;
            }
            flow+=addf;
            cost+=dist[t]*addf;
        }
    }
} g;


/*
    ans=A(n,4)-sum(  C(2,deg[i])  )*8*(n-3)
*/

char graph[205][205];
int deg[205];

int main(){
    int T; scanf("%d",&T);
    while(T--){
        int n; scanf("%d",&n);
        for(int i=1;i<=n;i++) deg[i]=0;
        for(int i=1;i<=n;i++) scanf("%s",graph[i]+1);


        int tot=n;
        int s=++tot;
        int t=++tot;
        for(int i=1;i<=n;i++) {
            for(int j=i+1;j<=n;j++){
                if(graph[i][j]=='1') deg[i]++;
                else if(graph[i][j]=='0') deg[j]++;
            }
        }
        int ans=0;
        for(int i=1;i<=n;i++) ans+=deg[i]*(deg[i]-1)/2;

        g.ini(n+2+n*(n-1)/2);
        for(int i=1;i<=n;i++) {
            for(int j=i+1;j<=n;j++){
                if(graph[i][j]=='2'){
                    g.add_edge(s,++tot,1,0);
                    g.add_edge(tot,i,1,0);
                    g.add_edge(tot,j,1,0);
                    g.add_edge(i,t,1,deg[i]);//C(n+1,2)-C(n,2)=n
                    g.add_edge(j,t,1,deg[j]);
                    deg[i]++;
                    deg[j]++;
                }
            }
        }
        int cost,flow;
        g.minCostMaxFlow(s,t,cost,flow);
        printf("%d\n", n*(n-1)*(n-2)*(n-3)-(ans+flow)*8*(n-3));

    }
}

hdu6611

转移自老blog

hdu6611

求k个不相交子序列,让其和最大。 使用dij费用流即可
做法一: 用主席树优化建图,用dp优化第一次dij算法
#include<bits/stdc++.h>
using namespace std;

typedef pair<int,int> pii;
struct MCMF{
    static const int maxn=2005*20*2,maxm=2+3*2005+2005*20*4;
    struct star{int v,nex,c,w;} edge[maxm<<1];

    int head[maxn],cnt,n;
    int pre[maxn],dist[maxn],h[maxn];// h -> 势能函数

    void ini(int point){
        cnt=-1;n=point;
        for(int i=0;i<=n;i++) head[i]=-1,h[i]=0;
    }
    void add_edge(int u, int v, int c, int w){
//        cout<<"     "<<u<<" "<<v<<" "<<c<<" "<<w<<endl;
        edge[++cnt]=star{v,head[u],c, w}; head[u]=cnt;
        edge[++cnt]=star{u,head[v],0,-w}; head[v]=cnt;
    }
    void minCostMaxFlow(int s, int t,int&flow,int&cost){//dij
        flow=cost=0;
        while(true){
            for(int i=0;i<=n;i++) dist[i]=2e9;
            dist[s]=0;
            priority_queue<pii,vector<pii>,greater<pii>>que; que.push(pii(dist[s],s));
            while(!que.empty()){
                int u=que.top().second,dis=que.top().first; que.pop();
                if (dist[u]!=dis) continue;
                for(int i=head[u];~i;i=edge[i].nex){
                    int v=edge[i].v,c=edge[i].c,w=edge[i].w;
                    if(c>0&&dist[v]>dist[u]+w+h[u]-h[v]){
                        dist[v]=dist[u]+w+h[u]-h[v];
//                        assert(dist[v]>=0);
                        pre[v]=i;
                        que.push(pii(dist[v],v));
                    }
                }
            }
            if(dist[t]==2e9) break;
            for(int i=0;i<=n;i++) h[i]+=dist[i];// d[i]=dist[i]+h[i]-h[s]=dist[i]+h[i]
            int addf=2e9;
            for(int x=t;x!=s;x=edge[pre[x]^1].v) addf=min(addf,edge[pre[x]].c);
            for(int x=t;x!=s;x=edge[pre[x]^1].v) {
                edge[pre[x]].c-=addf;
                edge[pre[x]^1].c+=addf;
            }
            flow+=addf;
            cost+=h[t]*addf;
        }
    }
}g;

int a[2005],n,k,s,t;

//主席树优化建图 dp优化dij
#define ml ((l+r)>>1)
#define mr (ml+1)
const int maxn = 2005;
int ls[maxn*20],rs[maxn*20],tot,rt[maxn];//update用了几次,就要乘以多少

void update(int pre,int&u,int l,int r,int pos,int dst){//把u按照pre复制,然后更新pos
    u=++tot;
    ls[u]=ls[pre];rs[u]=rs[pre];
    if(pre!=0) {
        g.add_edge((pre+n+2)<<1|1,(u+n+2)<<1,k,0); // k flow
        g.h[(u+n+2)<<1]=g.h[(u+n+2)<<1|1]=min(g.h[(pre+n+2)<<1],g.h[dst<<1|1]);
    }
    else g.h[(u+n+2)<<1]=g.h[(u+n+2)<<1|1]=g.h[dst<<1|1];
    g.add_edge(dst<<1|1,(u+n+2)<<1,k,0);// k flow
    g.add_edge((u+n+2)<<1,(u+n+2)<<1|1,k,0);// k flow
    if(l==r)return;
    if(pos<=ml) update(ls[pre],ls[u],l,ml,pos,dst);
    else update(rs[pre],rs[u],mr,r,pos,dst);
}

void query(int u,int l,int r,int pos,int dst){
    if(r<=pos){
        if(u!=0) {
            g.add_edge((u+n+2)<<1|1,dst<<1,k,0);// k flow
            g.h[dst<<1]=min(g.h[dst<<1],g.h[(u+n+2)<<1]);
        }
        g.h[dst<<1|1]=g.h[dst<<1]-pos;
        return;
    }
    if(l>pos) return;
    query(ls[u],l,ml,pos,dst);
    query(rs[u],mr,r,pos,dst);
}

void build_graph(){
    tot=0;
    g.ini(n*20*2);

    s=n+1,t=n+2;
    g.add_edge(s<<1,s<<1|1,k,0);// k flow
    g.h[s<<1]=g.h[s<<1|1]=0;

    int mim=0;
    for(int i=1;i<=n;i++) mim=max(mim,a[i]);

    for(int i=1;i<=n;i++) {
        g.add_edge(s<<1|1,i<<1,k,0);// k flow
        g.add_edge(i<<1,i<<1|1,1,-a[i]);// 1 flow
        g.add_edge(i<<1|1,t<<1,k,0);// k flow

        query(rt[i-1],1,mim,a[i],i);
        update(rt[i-1],rt[i],1,mim,a[i],i);
    }
    g.add_edge(t<<1,t<<1|1,k,0);// k flow
    g.h[t<<1]=g.h[t<<1|1]=g.h[(rt[n]+n+2)<<1];
}
///////////////////////////
//究极读入挂
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
    char ch=nc();int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}


int main(){
//    freopen("/Users/s/Downloads/2019HDOJ多校3_UESTC/data/1009/multi.in","r",stdin);
    int ti=read();
    while(ti--){
        n=read();k=read();
        for(int i=1;i<=n;i++) a[i]=read();

        build_graph();

        int flow,cost;
        g.minCostMaxFlow(s<<1,t<<1|1,flow,cost);
        printf("%d\n",-cost);
        //cout<<-cost<<endl;
    }
}
做法二:用主席树优化建图,用spfa优化第一次dij算法
#include<bits/stdc++.h>
using namespace std;

typedef pair<int,int> pii;
struct MCMF{
    static const int maxn=2005*20*2,maxm=2+3*2005+2005*20*4;
    struct star{int v,nex,c,w;} edge[maxm<<1];

    int head[maxn],cnt,n;
    int pre[maxn],dist[maxn],h[maxn];// h -> 势能函数

    void ini(int point){
        cnt=-1;n=point;
        for(int i=0;i<=n;++i) head[i]=-1,h[i]=0;
    }
    void add_edge(int u, int v, int c, int w){
//        cout<<"     "<<u<<" "<<v<<" "<<c<<" "<<w<<endl;
        edge[++cnt]=star{v,head[u],c, w}; head[u]=cnt;
        edge[++cnt]=star{u,head[v],0,-w}; head[v]=cnt;
    }
    void minCostMaxFlow_dij(int s, int t,int&flow,int&cost){//dij
        flow=cost=0;
        while(true){
            for(int i=0;i<=n;++i) dist[i]=2e9;
            dist[s]=0;
            priority_queue<pii,vector<pii>,greater<pii>>que; que.push(pii(dist[s],s));
            while(!que.empty()){
                int u=que.top().second,dis=que.top().first; que.pop();
                if (dist[u]!=dis) continue;
                for(int i=head[u];~i;i=edge[i].nex){
                    int v=edge[i].v,c=edge[i].c,w=edge[i].w;
                    if(c>0&&dist[v]>dist[u]+w+h[u]-h[v]){
                        dist[v]=dist[u]+w+h[u]-h[v];
//                        assert(dist[v]>=0);
                        pre[v]=i;
                        que.push(pii(dist[v],v));
                    }
                }
            }
            if(dist[t]==2e9) break;
            for(int i=0;i<=n;++i) h[i]+=dist[i];// d[i]=dist[i]+h[i]-h[s]=dist[i]+h[i]
            int addf=2e9;
            for(int x=t;x!=s;x=edge[pre[x]^1].v) addf=min(addf,edge[pre[x]].c);
            for(int x=t;x!=s;x=edge[pre[x]^1].v) {
                edge[pre[x]].c-=addf;
                edge[pre[x]^1].c+=addf;
            }
            flow+=addf;
            cost+=h[t]*addf;
        }
    }
    void minCostMaxFlow(int s, int t,int&flow,int&cost){//spfa 用来先负权图处理
        for(int i=0;i<=n;++i) dist[i]=2e9;
        dist[s]=0;
        queue<pii>que; que.push(pii(dist[s],s));
        while(!que.empty()){
            int u=que.front().second,dis=que.front().first; que.pop();
            if (dist[u]!=dis) continue;
            for(int i=head[u];~i;i=edge[i].nex){
                int v=edge[i].v,c=edge[i].c,w=edge[i].w;
                if(c>0&&dist[v]>dist[u]+w){
                    dist[v]=dist[u]+w;
                    pre[v]=i;
                    que.push(pii(dist[v],v));
                }
            }
        }
        if(dist[t]==2e9) return;
        for(int i=0;i<=n;++i) h[i]=dist[i];// d[i]=dist[i]+h[i]-h[s]=dist[i]+h[i]
        int addf=2e9;
        for(int x=t;x!=s;x=edge[pre[x]^1].v) addf=min(addf,edge[pre[x]].c);
        for(int x=t;x!=s;x=edge[pre[x]^1].v) {
            edge[pre[x]].c-=addf;
            edge[pre[x]^1].c+=addf;
        }
        int oldh=h[t];
        minCostMaxFlow_dij(s,t,flow,cost);
        flow+=addf;
        cost+=oldh*addf;
    }
}g;

int a[2005],n,k,s,t;

//主席树优化建图 dp优化dij
#define ml ((l+r)>>1)
#define mr (ml+1)
const int maxn = 2005;
int ls[maxn*20],rs[maxn*20],tot,rt[maxn];//update用了几次,就要乘以多少

void update(int pre,int&u,int l,int r,int pos,int dst){//把u按照pre复制,然后更新pos
    u=++tot;
    ls[u]=ls[pre];rs[u]=rs[pre];
    if(pre!=0) g.add_edge((pre+n+2)<<1|1,(u+n+2)<<1,k,0); // k flow
    g.add_edge(dst<<1|1,(u+n+2)<<1,k,0);// k flow
    g.add_edge((u+n+2)<<1,(u+n+2)<<1|1,k,0);// k flow
    if(l==r)return;
    if(pos<=ml) update(ls[pre],ls[u],l,ml,pos,dst);
    else update(rs[pre],rs[u],mr,r,pos,dst);
}

void query(int u,int l,int r,int pos,int dst){
    if(r<=pos){
        if(u!=0)g.add_edge((u+n+2)<<1|1,dst<<1,k,0);// k flow
        return;
    }
    if(l>pos) return;
    query(ls[u],l,ml,pos,dst);
    query(rs[u],mr,r,pos,dst);
}

void build_graph(){
    tot=0;
    g.ini(n*20*2);

    s=n+1,t=n+2;
    g.add_edge(s<<1,s<<1|1,k,0);// k flow

    int mim=0;
    for(int i=1;i<=n;++i) mim=max(mim,a[i]);

    for(int i=1;i<=n;++i) {
        g.add_edge(s<<1|1,i<<1,k,0);// k flow
        g.add_edge(i<<1,i<<1|1,1,-a[i]);// 1 flow
        g.add_edge(i<<1|1,t<<1,k,0);// k flow

        query(rt[i-1],1,mim,a[i],i);
        update(rt[i-1],rt[i],1,mim,a[i],i);
    }
    g.add_edge(t<<1,t<<1|1,k,0);// k flow
}

///////////////////////////
//究极读入挂
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
    char ch=nc();int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}


int main(){
//    freopen("/Users/s/Downloads/2019HDOJ多校3_UESTC/data/1009/multi.in","r",stdin);
    int ti=read();
    while(ti--){
        n=read();k=read();
        for(int i=1;i<=n;++i) a[i]=read();

        build_graph();

        int flow,cost;
        g.minCostMaxFlow(s<<1,t<<1|1,flow,cost);
        printf("%d\n",-cost);
    }
}

poj3177

转移自老blog

zoj4097

题意:
模版题,给你一幅图,问你最少加几条边,使得图变成一个双联通分量。
模版
// #include<bits/stdc++.h>
#include<iostream>
#include<cstdio>

using namespace std;

struct Graph{
    static const int maxn=1e5+5, maxm=3e5+5;
    struct star{int v,nex;}edge[maxm<<1];
    int head[maxn],d[maxn],cnt;
    void ini(int n){
        for(int i=0;i<=n;i++) head[i]=-1;
        for(int i=0;i<=n;i++) d[i]=0;
        cnt=-1;
    }
    void add_edge(int u,int v){
        edge[++cnt]=star{v,head[u]};
        head[u]=cnt;
        edge[++cnt]=star{u,head[v]};
        head[v]=cnt;
        d[u]++;
        d[v]++;
    }
}tree;

struct Tarjan:Graph{//双联通分量, 割边, 桥, 边双联通缩点
    struct Bridge{int u,v;}bridge[maxn];
    int dfn[maxn],low[maxn],belong[maxn],vis[maxn],sta[maxn],sta_,nums,bridge_;
    void ini(int n){
        for(int i=0;i<=n;i++) vis[i]=0;
        bridge_=0;
        nums=0;
        Graph::ini(n);
    }
    void tarjan(int u,int father,int&step){
        low[u]=dfn[u]=++step;
        sta[++sta_]=u;
        vis[u]=1;
        bool firsttimes=true;//用于判重边
        for(int i=head[u];~i;i=edge[i].nex){
            int v=edge[i].v;
            if(v==father&&firsttimes) {
                firsttimes=false;
                continue;
            }//父边
            if(vis[v]==1) low[u]=min(low[u],dfn[v]);//回边,终点在栈中
            else {//树边
                tarjan(v,u,step);
                low[u]=min(low[u],low[v]);
                if(low[v]>dfn[u]) bridge[++bridge_]=Bridge{u,v};
            }
        }
        if(low[u]==dfn[u]){
            while(sta[sta_]!=u) belong[sta[sta_--]]=nums+1;
            belong[sta[sta_--]]=++nums;
        }
    }
}graph;

inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}

int main(){
    int n=read(),m=read();
    graph.ini(n);
    for(int i=0;i<m;i++) graph.add_edge(read(),read());
    int step=0;
    graph.tarjan(1,0,step);

    tree.ini(graph.nums);
    for(int i=1;i<=graph.bridge_;i++){
        int u=graph.bridge[i].u,v=graph.bridge[i].v;
        tree.add_edge(graph.belong[u],graph.belong[v]);
    }
    int ans=0;
    for(int i=1;i<=graph.nums;i++){
        if(tree.d[i]==1) {
            ans++;
        }
    }
    cout<<(ans+1)/2<<endl;
}

uoj111

转移自老blog

uoj111

优化建图即可,对图分块,但是这样还是过不了,因为常数过大,不建图跑dij还是会tle,要用spfa才能过
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
typedef pair<int,int> pii;

int main(){
    int n,m; scanf("%d%d",&n,&m);
    int sqr=200;
    vector<vector<int> >dog(n),havedog(n,vector<int>(sqr,0));
    int src=0,dst=1;
    for(int i=0;i<m;i++) {
        int x,y; scanf("%d%d",&x,&y);
        if(i==0) src=x;
        if(i==1) dst=x;
        dog[x].push_back(y);// a dog at x can jump y step
        if(y<sqr)havedog[x][y]=1;
    }
    for(int i=0;i<n;i++) {
        sort(dog[i].begin(),dog[i].end());
        dog[i].erase(unique(dog[i].begin(),dog[i].end()),dog[i].end());
    }
    // dij
    vector<int>dist(n,2e9);
    __gnu_pbds::priority_queue<pii,greater<pii>,__gnu_pbds::thin_heap_tag> q;
    dist[src]=0;
    q.push(pii(dist[src],src));
    while(!q.empty()){
        int u=q.top().second,dis=q.top().first;
        q.pop();
        if(dist[u]!=dis)continue;
        for(int jump:dog[u]){
            for(int d=1;u+d*jump<n;d++){
                if(dist[u+d*jump]>dist[u]+d){
                    dist[u+d*jump]=dist[u]+d;
                    q.push(pii(dist[u+d*jump],u+d*jump));
                }
                if(jump<sqr&&havedog[u+d*jump][jump]) break;
            }
            for(int d=1;u-d*jump>=0;d++){
                if(dist[u-d*jump]>dist[u]+d){
                    dist[u-d*jump]=dist[u]+d;
                    q.push(pii(dist[u-d*jump],u-d*jump));
                }
                if(jump<sqr&&havedog[u-d*jump][jump]) break;
            }
        }
    }
    if(dist[dst]==2e9) cout<<-1<<endl;
    else cout<<dist[dst]<<endl;
}

uoj67

转移自老blog

uoj67

题意:
        辞旧迎新之际,喜羊羊正在打理羊村的绿化带,然后他发现了一棵长着毒瘤的树。         这个长着毒瘤的树可以用 n 个结点 m 条无向边的无向图表示。这个图中有一些结点被称作是毒瘤结点,即删掉这个结点和与之相邻的边之后,这个图会变为一棵树。树也即无简单环的无向连通图。         现在给你这个无向图,喜羊羊请你帮他求出所有毒瘤结点。

输入:
        第一行两个正整数 n,m ,表示有 n 个点 m 条边。保证 n≥2 。 接下来 m 行,每行两个整数 v,u ,表示 v 和 u 之间有一条无向边。1≤v,u≤n 。保证没有重边和自环。
        删掉一个点后,也就删掉了与他相连的所有边,记录这些边点条数(点的度数),删掉后成为树的充要条件,剩下n-2条边 且删除的点不是割点。
#include<bits/stdc++.h>
using namespace std;

struct Graph{
    static const int maxn=1e5+5, maxm=3e5+5;
    struct star{int v,nex;}edge[maxm<<1];
    int head[maxn],cnt,n;
    int d[maxn];
    void ini(int n){
        this->n=n; cnt=-1;
        for(int i=0;i<=n;i++) head[i]=-1;
    }
    void add_edge(int u,int v){
        edge[++cnt]=star{v,head[u]}; head[u]=cnt;
        edge[++cnt]=star{u,head[v]}; head[v]=cnt;
        d[u]++;
        d[v]++;
    }
};

struct Tarjan:Graph{//割点
    int low[maxn],dfn[maxn],cut[maxn];
    int step;
    void tarjan(){
        step=0;
        for(int i=0;i<=n;i++) dfn[i]=cut[i]=0;
        for(int i=1;i<=n;i++) if(dfn[i]==0) tarjan(i,0);//多个联通快
    }
    void tarjan(int u,int father=0){//此函数不开放
        low[u]=dfn[u]=++step;
        int first=1, son=0;
        for(int i=head[u];~i;i=edge[i].nex){
            int v=edge[i].v;
            if(v==father&&!first) first=false;
            else if(dfn[v]) low[u]=min(low[u],dfn[v]);
            else{
                son++;
                tarjan(v,u);
                low[u]=min(low[u],low[v]);
                //一个顶点u是割点,当且仅当1或2
                //1.u为树根且u有多与一个子树
                //2.u不为树根且存在边(u,v)为树边,使得dfn[u]<=low[v]
                if(father!=0&&dfn[u]<=low[v]) cut[u]=1;
                if(father==0&&son>1) cut[u]=1;
            }
        }
    }
}graph;

inline int read(){
    int ret=0,f=1;
    char ch=getchar();
    while('0'>ch||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while('0'<=ch&&ch<='9'){
        ret=(ret<<1)+(ret<<3)+(ch^48);
        ch=getchar();
    }
    return ret;
}

int main(){
    int n=read();
    int m=read();
    graph.ini(n);
    for(int i=0;i<m;i++) graph.add_edge(read(),read());

    graph.tarjan();

    vector<int>ans;

    for(int i=1;i<=n;i++) {
        if(!graph.cut[i]&&(m-graph.d[i])==n-2){
            ans.push_back(i);
        }
    }

    cout<<ans.size()<<endl;
    for(int i=0;i<ans.size();i++) {
        printf("%d", ans[i]);
        if(i+1==ans.size()) printf("\n");
        else printf(" ");
    }


}