模板库&&一些小技巧?

IO

 
namespace IO{
    char buf[1000010],*cur=buf+1000010;
    inline char getc(){
        (cur==buf+1000010)?fread(cur=buf,1,1000010,stdin):0;
        return *cur++;
    }
    char buff[1000010],*curr=buff;
    inline void flush(){
        fwrite(buff,1,curr-buff,stdout);
    }
    inline void putc(const char &ch){
        (curr==buff+1000010)?fwrite(curr=buff,1,1000010,stdout):0;
        *curr++=ch;
    }
  
    inline void rd(int &x){
        x=0;char ch=getc();int f=1;
        while(ch<'0'||ch>'9'){
            if(ch=='-')f=-1;
            ch=getc();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';
            ch=getc();
        }
        x*=f;
    }
 
    char st[60];int tp;
    void PT(int x){
        if(x==0)putc('0');
        else{
            while(x>0){
                st[++tp]=x%10+'0';
                x/=10;
            }
        }
        while(tp)putc(st[tp--]);
    }
}
using IO::getc;
using IO::putc;
using IO::rd;
using IO::PT;

tarjan

强连通分量
来自这里

void tarjan(int now)
{
    dfn[now]=low[now]=++cnt;  //初始化
    stack[++t]=now;       //入栈操作
    v[now]=1;            //v[]代表该点是否已入栈
    for(int i=f[now];i!=-1;i=e[i].next)  //邻接表存图
        if(!dfn[e[i].v])           //判断该点是否被搜索过
        {
            tarjan(e[i].v);
            low[now]=min(low[now],low[e[i].v]); //回溯时更新low[ ],取最小值
        }
        else if(v[e[i].v])
            low[now]=min(low[now],dfn[e[i].v]); //一旦遇到已入栈的点,就将该点作为连通量的根
                             //这里用dfn[e[i].v]更新的原因是:这个点可能
                             //已经在另一个强连通分量中了但暂时尚未出栈,所
                             //以now不一定能到达low[e[i].v]但一定能到达
                             //dfn[e[i].v].
    if(dfn[now]==low[now])
    {
        int cur;
        do
        {
            cur=stack[t--];
            v[cur]=false;                       //不要忘记出栈
        }while(now!=cur);
    }
}

求割点
来自这里

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100009;
struct node {
    int to,nxt;
} e[2*maxn];
int n,m,cnt=0,index=0,head[maxn],dfn[maxn],low[maxn],iscut[maxn],ans=0;
void add(int u,int v) {
    e[++cnt].to=v;
    e[cnt].nxt=head[u];
    head[u]=cnt;
}
void tarjan(int u,int fa) {
    int child=0;
    dfn[u]=low[u]=++index;
//	cout<<"dfs  "<<dfn[u]<<" "<<low[u]<<endl;
    for(int i=head[u]; i>0; i=e[i].nxt) {
        int v=e[i].to;
        if(!dfn[v]) {
            child++;
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(fa>0&&low[v]>=dfn[u]) {
                if(!iscut[u])++ans;//一个顶点可能被标记多次
                iscut[u]=1;
                //	cout<<"df u"<<u<<" ans"<<ans<<endl;
            }
 
        } else if(dfn[v]<dfn[u]&&v!=fa)
            low[u]=min(low[u],dfn[v]);
    }
    //cout<<u<<"  child  "<<child<<endl;
    if(fa<0&&child>1) {
        //if(!iscut[u])
        ++ans;//根节点不可能被统计多次
        iscut[u]=1;
        //	cout<<"fa "<<u<<endl;
    }
 
}
int main() {
    cin>>n>>m;
    int u,v;
    for(int i=1; i<=m; i++) {
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    for(int i=1; i<=n; i++)
        if(!dfn[i])tarjan(i,-1);
    printf("%d\n",ans);
    for(int i=1; i<=n; i++)
        if(iscut[i])cout<<i<<" ";
}

求割边
来自这里

void Tarjan(int u,int fa)
{
    dfn[u] = low[u] = ++indx;
    stk.push(u);
    int flg = 0;
    for(int i=0; i<vt[u].size(); ++i)
    {
        int v = vt[u][i];
        if(!flg && v == fa)
        {
            flg = 1;
            continue;
        }
        if(!dfn[v])
        {
            Tarjan(v,u);
            low[u] = min(low[u],low[v]);
            //if(low[v]>dfn[u])the edge cut
        }
        else
            low[u] = min(low[u],dfn[v]);
    }

    if(low[u] == dfn[u])
    {
        num++;
        int v;
        do
        {
            v = stk.top();
            stk.pop();
            belong[v] = num;
        }while(u != v);
    }
}

点双

 
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
#define Maxn 1000010
int head[Maxn],v[Maxn<<1],nxt[Maxn<<1],tot=0;
inline int id(int i){
    if(i&1)i++;
    return i/2;
}
inline void add_edge(int s,int e){
    tot++;v[tot]=e;nxt[tot]=head[s];head[s]=tot;
    tot++;v[tot]=s;nxt[tot]=head[e];head[e]=tot;
}
 
int scc[Maxn],cnt;
bool ans[Maxn];
int stack[Maxn],top;
int dfn[Maxn],low[Maxn],dfk;
int hd[Maxn],to[Maxn],la[Maxn],out[Maxn],len=0;
int seq[Maxn];
inline void link(int s,int e){
    len++;to[len]=e;la[len]=hd[s];hd[s]=len;
}
void tarjan(int u,int f){
    dfn[u]=low[u]=++dfk;
    for(int i=head[u];i;i=nxt[i])
        if(v[i]!=f){
            if(!dfn[v[i]]){
            stack[++top]=i;
            tarjan(v[i],u);
            low[u]=min(low[u],low[v[i]]);
            if(low[v[i]]>=dfn[u]){
                int pre=top;
                int s,e,H=0,res=0;
                cnt++;
                do{
                    e=v[stack[top]];
                    if(stack[top]&1)s=v[stack[top]+1];
                    else s=v[stack[top]-1];
                    res++;
                    seq[res]=stack[top];
                    if(scc[s]!=cnt){
                        scc[s]=cnt;
                        H++;
                        if(s!=u){
                            for(int j=hd[s];j;j=la[j])
                                seq[++res]=to[j];
                        }
                    }
                    if(scc[e]!=cnt){
                        scc[e]=cnt;
                        H++;
                        if(e!=u){
                            for(int j=hd[e];j;j=la[j])
                                seq[++res]=to[j];
                        }
                    }
                    top--;
                }while(s!=u||e!=v[i]);
                if(res==H)
                    for(int j=1;j<=res;++j)ans[id(seq[j])]=true;
            }
        }else{
            low[u]=min(low[u],dfn[v[i]]);
            if(dfn[v[i]]<dfn[u]){
            link(u,i);
            out[u]++;
        }
        }
        }   
}
 
inline void rd(int &x){
    x=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
}
 
int main(){
    rd(n);rd(m);
    int s,e;
    for(register int i=1;i<=m;++i){
        rd(s);rd(e);
        add_edge(s,e);
    }
    tarjan(1,0);
    int Ans=0;
    for(register int i=1;i<=m;++i)
        if(ans[i])Ans^=i;
    printf("%d\n",Ans);
    return 0;
}/*
4 4
1 2
2 4
4 3
3 2
*/

点双之间的交点是割点
一些相关结论

AC自动机

void Build_AC(){
    tree[root].fail=0;
    Q.push(root);
    int to;
    while(!Q.empty()){
        int k=Q.front();
        Q.pop();
        for(int i=0;i<26;++i){
            if(k==root)to=root;
            else to=tree[tree[k].fail].son[i];
            if(tree[k].son[i]){
                tree[tree[k].son[i]].fail=to;
                Q.push(tree[k].son[i]);
            }else tree[k].son[i]=to;
        }
    }
}

### 

LCT

void push_down(int u){
    int ls=tree[u].son[0],rs=tree[u].son[1];
    if(tree[u].rev){
        tree[ls].rev^=1;
        tree[rs].rev^=1;
        swap(tree[u].son[0],tree[u].son[1]);
        tree[u].rev=0;
    }
    int num1=tree[u].md,num2=tree[u].ad;
    if(num1!=1||num2!=0){
        calc(ls,num1,num2);
        calc(rs,num1,num2);
        tree[u].md=1;tree[u].ad=0;
    }
}
void rot(int u){
    int f=tree[u].fa,ff=tree[f].fa;
    int dir1=who(u),dir2=dir1^1;
    if(!rt(f))tree[ff].son[who(f)]=u;
    tree[u].fa=ff;
    tree[f].son[dir1]=tree[u].son[dir2];
    tree[tree[u].son[dir2]].fa=f;
    tree[u].son[dir2]=f;
    tree[f].fa=u;
    push_up(f);
    push_up(u);
}
void Splay(int u){ 
    st.push(u);
    for(int x=u;!rt(x);x=tree[x].fa)st.push(tree[x].fa);
    while(!st.empty()){push_down(st.top());st.pop();}
    for(int fa;!rt(u);rot(u)){  
        if(!rt(fa=tree[u].fa)){  
            rot((who(u)==who(fa))?fa:u);  
        }  
    }  
}
void access(int u){
    for(int t=0;u;t=u,u=tree[u].fa){
        Splay(u);
        tree[u].son[1]=t;
        push_up(u);
    }
}
void makeroot(int u){
    access(u);
    Splay(u);
    tree[u].rev^=1;
}
void link(int s,int e){
    makeroot(s);
    tree[s].fa=e;
}
void cut(int s,int e){
    makeroot(s);
    access(e);
    Splay(e);
    tree[e].son[0]=tree[s].fa=0;
    push_up(e);
}
void spilt(int s,int e){
    makeroot(e);
    access(s);
    Splay(s);
}

转载自GXZ的博客
事实上,我们注意到,对于Splay Tree的所有基本操作,除了access和link以外,都不会对虚儿子的信息进行修改。
access操作中割断了实边c[1][x],该边变为了虚边,所以应该加到x的虚儿子信息中,加入了实边t,该边不再是虚边,所以应从x的虚儿子信息中减去。
link操作中为了在加入x时同时更新y的信息,需要makeroot(x),makeroot(y),然后连x->y的虚边(实际上只需要access(y)和splay(y))。
其余的操作,和普通的LCT没有任何区别。

SJ定理

(1)SG==0,有某单一游戏的SG>1。

(2)SG!=0,有某单一游戏的SG>1。(必胜SJ)

(3)SG==0,无某单一游戏的SG>1。(必胜SJ)

(4)SG!=0,无某单一游戏的SG>1。

prufer序列

对于n个点的标号无根树,其方案数为
( n − 2 ) ! ∏ ( d i − 1 ) ! \frac{(n-2)!}{\prod (d_i-1)!} (di1)!(n2)!

BM

namespace BM{
	int fail[N<<1],dt[N<<1],cur=0,a[N<<1],cnt=0,Bst=0;
	poly f[2],bst;
	void ins(int n){
		dt[n]=a[n];
		for(int i=1;i<f[cur].size();++i)dt[n]=(dt[n]-1ll*a[n-i]*f[cur][i]%Mod+Mod)%Mod;
		if(!dt[n])return;
		fail[cnt++]=n;
		cur^=1;
		if(cnt==1){
			f[cur].resize(n+1,0);
			return;
		}
		int t=1ll*dt[n]*Fast_Pow(dt[fail[Bst]],Mod-2)%Mod;
		f[cur].resize(0);f[cur].resize(n-fail[Bst]);f[cur].push_back(t);
		if(t)t=Mod-t;
		for(int i=1;i<bst.size();++i)f[cur].push_back(1ll*bst[i]*t%Mod);
		if(f[cur].size()>=f[cur^1].size())Bst=cnt-1,bst=f[cur^1];
		f[cur]+=f[cur^1];
	}
	inline poly solve(int *_a,int n){
		cur=0;cnt=0;Bst=0;bst.resize(0);f[0].resize(0);f[1].resize(0);
		for(int i=1;i<=n;++i)a[i]=_a[i],ins(i);
		return f[cur];
	}
}

回文自动机

namespace PAM{
	int ed[Maxn];
	int ch[Maxn][26],fail[Maxn],len[Maxn],fa[Maxn];
    int last,tot,l;
    char s[Maxn];
	
    void init(){
	    last=0;tot=1;l=0;ed[0]=0;
	    memset(ch[0],0,sizeof(ch[0]));
        memset(ch[1],0,sizeof(ch[1]));
        fail[0]=1;
        len[1]=-1;
        s[0]=-1;
        fa[0]=fa[1]=0;
        cnt=0;root[0]=root[1]=0;
    }
    int getfail(int x){
	    while(s[l-len[x]-1]!=s[l])x=fail[x];
	    return x;
    }
    void Add(char c){
    	l++;
    	s[l]=c;
    	int dir=c-'a';
    	int tmp=getfail(last);
    	if(!ch[tmp][dir]){
    		tot++;
    		memset(ch[tot],0,sizeof(ch[tot]));
		    len[tot]=len[tmp]+2;
		    fa[tot]=tmp;
		    fail[tot]=ch[getfail(fail[tmp])][dir];
		    Insert(root[tot],root[fail[tot]],1,N,len[tot]);
		    ch[tmp][dir]=tot;
		}
		last=ch[tmp][dir];
	    ed[l]=last;
	}
}

稳定婚姻匹配问题

第一轮,每个男人都选择自己名单上排在首位的女人,并向她表白。这种时候会出现两种情况:
(1)该女士还没有被男生追求过,则该女士接受该男生的请求。
(2)若该女生已经接受过其他男生的追求,那么该女生会将该男士与她的现任男友进行比较,若更
喜欢她的男友,那么拒绝这个人的追求,否则,抛弃其男友
证明&&相关材料

void GaleShapley(const int (&man)[N][N],const int (&woman)[N][N],int (&match)[N]){
    int wm[N][N];    // wm[i][j]: rank from girl i to boy j
    int choose[N];    // choose[i]: current boyfriend of girl i
    int manIndex[N]; //    manIndex[i]: how many girls that have rejected boy i
    int i,j;
    int w,m;
    for(i=0;i<N;i++){
        match[i]=-1;
        choose[i]=-1;
        manIndex[i]=0;
        for(j=0;j<N;j++)
            wm[i][woman[i][j]]=j;
    }

    bool bSingle=false;
    while(!bSingle){
        bSingle=true;
        for(i=0;i<N;i++){
            if(match[i]!=-1) // boy i already have a girlfriend
                continue;
            bSingle=false;
            j=manIndex[i]++; // the jth girl that boy i like most
            w=man[i][j];    
            m=choose[w];    // current girl w's boyfriend
            if(m==-1 || wm[w][i]<wm[w][m]){ // if girl w prefer boy i
                match[i]=w;
                choose[w]=i;
                if(m!=-1)
                    match[m]=-1;
            }
        }
    }
}

三角恒等式

cos(α+β)=cosα·cosβ-sinα·sinβ
cos(α-β)=cosα·cosβ+sinα·sinβ
sin(α±β)=sinα·cosβ±cosα·sinβ
tan(α+β)=(tanα+tanβ)/(1-tanα·tanβ)
tan(α-β)=(tanα-tanβ)/(1+tanα·tanβ)

最大流

这里是实现最大闭合权子图

namespace Graph{
	int d[1005],head[1005],cur[1005],v[10005],nxt[10005],w[10005],tot=0;
	int Q[1005],hd,tl,S,T;
	void init_test(){memset(d,-1,sizeof(d));}
	void init_graph(){
		for(int i=1;i<=tot;++i)head[v[i]]=0;
		tot=0;
	}
	bool Bfs(){
		for(int i=0;i<tl;++i)d[Q[i]]=-1;
		hd=tl=0;
		Q[tl++]=S;d[S]=0;
		while(hd<tl){
			int u=Q[hd];hd++;
			for(int i=head[u];i;i=nxt[i])
				if(w[i]&&d[v[i]]<0){
					d[v[i]]=d[u]+1;
					Q[tl++]=v[i];
					if(v[i]==T)return true;
				}
		}
		return false;
	}
	int find(int u,int flow){
		if(u==T||!flow)return flow;
		int res=0,minv;
		for(int &i=head[u];i;i=nxt[i])
			if(w[i]&&d[v[i]]==d[u]+1&&(minv=find(v[i],min(flow,w[i])))){
				w[i]-=minv;
				if(i&1)w[i+1]+=minv;
				else w[i-1]+=minv;
				res+=minv;
				flow-=minv;
				if(!flow)break;
			}
		return res;
	}
	void add_edge(int s,int e,int t){
		tot++;v[tot]=e;w[tot]=t;nxt[tot]=head[s];head[s]=tot;
		tot++;v[tot]=s;w[tot]=0;nxt[tot]=head[e];head[e]=tot;
	}
	int max_flow(){
		int res=0;
		memcpy(cur,head,sizeof(int)*(T+1));
		while(Bfs()){
			res+=find(S,inf);
			memcpy(head,cur,sizeof(int)*(T+1));
		}
		return res;
	}
	int calc(){
		S=n+1;T=n+2;int res=0;
		for(int i=1;i<=n;++i)
			if(a[i]>=0){
				add_edge(S,i,a[i]);
				res+=a[i];
			}else add_edge(i,T,-a[i]);
		res-=max_flow();
		return res;
	}
}

单调区间,可合并

对于一类可合并的单调区间询问问题
可以维护两个栈st1,st2
st2存着当前的答案,加入好处理
st1存着每个后缀的答案,删除好处理
每次合并好st1的后缀和st2的答案
st1空了就把st2的元素移到st1去,st2变空

费用流三大写法

EK,ZKW,原始对偶

#include<bits/stdc++.h>
using namespace std;
const int inf=1000000000;
int n,m,S,T;
#define Maxn 5005
#define E 100010
#define cout cerr
int maxf=0,Ans=0;
int head[Maxn],cap[E],v[E],w[E],nxt[E],tot=0;
inline void add_edge(int s,int e,int c,int t){
	tot++;v[tot]=e;cap[tot]=c;w[tot]=t;nxt[tot]=head[s];head[s]=tot;
	tot++;v[tot]=s;cap[tot]=0;w[tot]=-t;nxt[tot]=head[e];head[e]=tot;
}

namespace EK{
	int Q[E],hd,tl;
	int dis[Maxn];
	int inq[Maxn],inqT=0,vis[Maxn],visT=0;
	int FR[Maxn];
	bool SPFA(){
		hd=tl=0;
		inqT++;
		for(int i=1;i<=n;++i)dis[i]=inf;
		dis[S]=0;Q[tl++]=S;inq[S]=inqT;
		while(hd<tl){
			int u=Q[hd];
			hd++;
			inq[u]=0;
			for(int i=head[u];i;i=nxt[i])
				if(cap[i]&&dis[u]+w[i]<dis[v[i]]){
					FR[v[i]]=i;
					dis[v[i]]=dis[u]+w[i];
					if(inq[v[i]]!=inqT){
						inq[v[i]]=inqT;
						Q[tl++]=v[i];
					}
				}
		}
		return dis[T]!=inf; 
	}
	int search(int u,int flow){
		if(u==T||!flow){
			maxf+=flow;
			return flow;
		}
		vis[u]=visT;
		int res=0;
		for(int i=head[u];i&&flow;i=nxt[i])
			if(cap[i]&&dis[u]+w[i]==dis[v[i]]&&vis[v[i]]!=visT){
				int t=search(v[i],min(flow,cap[i]));
				Ans+=t*w[i];
				cap[i]-=t;
				if(i&1)cap[i+1]+=t;
				else cap[i-1]+=t;
				res+=t;
				flow-=t;
			}
		return res;
	}
	void solve(){
		while(SPFA()){
			/*int u=T;int mn=inf;
			while(u!=S){
				int t=FR[u];
				mn=min(mn,cap[t]);
				if(t&1)u=v[t+1];
				else u=v[t-1];
			}
			maxf+=mn;
			u=T;
			while(u!=S){
				int t=FR[u];
				cap[t]-=mn;
				Ans+=mn*w[t];
				if(t&1){
					cap[t+1]+=mn;
					u=v[t+1];
				}else{
					cap[t-1]+=mn;
					u=v[t-1];
				}
			}*/
			visT++;
			search(S,inf);
		}
	}
}

namespace zkw{
	//边权为负记得跑一遍SPFA 
	int dis[Maxn],vis[Maxn],visT=0;
	bool flag;
	int aug(int u,int flow){
		if(u==T){flag=true;maxf+=flow;Ans-=flow*dis[S];return flow;}
		vis[u]=visT;
		int res=0;
		for(int i=head[u];i&&flow;i=nxt[i])
			if(cap[i]&&dis[v[i]]==dis[u]+w[i]&&vis[v[i]]!=visT){
				int t=aug(v[i],min(flow,cap[i]));
				cap[i]-=t;
				if(i&1)cap[i+1]+=t;
				else cap[i-1]+=t;
				res+=t;
				flow-=t;
			}
		return res;
	} 
	bool mdf(){
		if(vis[T]==visT)return true;
		int mn=inf;
		for(int u=1;u<=n;++u)
			if(vis[u]==visT)
				for(int i=head[u];i;i=nxt[i])
					if(vis[v[i]]!=visT&&cap[i])mn=min(mn,dis[u]-dis[v[i]]+w[i]);
		if(mn==inf)return false;
		for(int i=1;i<=n;++i)
			if(vis[i]==visT)dis[i]-=mn;
		return true;
	}
	void solve(){
		memset(dis,0,sizeof(int)*(n+1));
		do{
			visT++;
			flag=false;
			aug(S,inf);
			if(flag)vis[T]=visT;
		}while(mdf());
	}
}

namespace PD{
	///边权为负记得跑一遍SPFA 
	int D[Maxn],dis[Maxn];
	int vis[Maxn],visT;
	struct HeapNode{
		int u,d;
		HeapNode(){u=d=0;}
		HeapNode(int _u,int _d){u=_u;d=_d;}
		bool operator <(const HeapNode &t)const{return d>t.d;}
	};
	priority_queue<HeapNode> Q;
	bool Dij(){
		for(int i=1;i<=n;++i)dis[i]=inf;
		while(!Q.empty())Q.pop();
		dis[S]=0;Q.push(HeapNode(S,0));
		while(!Q.empty()){
			HeapNode node=Q.top();
			Q.pop();
			int u=node.u;
			if(dis[u]!=node.d)continue;
			for(int i=head[u];i;i=nxt[i])
				if(cap[i]&&dis[u]+D[u]+w[i]-D[v[i]]<dis[v[i]]){
					dis[v[i]]=dis[u]+D[u]+w[i]-D[v[i]];
					Q.push(HeapNode(v[i],dis[v[i]]));
				}
		}
		return dis[T]!=inf;
	}
	int aug(int u,int flow){
		if(u==T||!flow){maxf+=flow;return flow;}
		vis[u]=visT;
		int res=0;
		for(int i=head[u];i;i=nxt[i])
			if(cap[i]&&dis[v[i]]==dis[u]+D[u]+w[i]-D[v[i]]&&vis[v[i]]!=visT){
				int t=aug(v[i],min(flow,cap[i]));
				Ans+=t*w[i];
				cap[i]-=t;
				if(i&1)cap[i+1]+=t;
				else cap[i-1]+=t;
				res+=t;
				flow-=t;
			}
		return res;
	}
	void solve(){
		memset(D,0,sizeof(int)*(n+1));
		while(Dij()){
			visT++;
			aug(S,inf);
			for(int i=1;i<=n;++i)
				if(dis[i]==inf)D[i]=inf;
				else D[i]+=dis[i];
		}
	}
}

inline void rd(int &x){
	x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
}

int main(){
	rd(n);rd(m);rd(S);rd(T);
	int s,e,c,t;
	for(int i=1;i<=m;++i){
		rd(s);rd(e);rd(c);rd(t);
		add_edge(s,e,c,t);
	}
	PD::solve();
	printf("%d %d\n",maxf,Ans);
	return 0;
}

半平面交

bool check(Line L,Line a,Line b){
    Point cro=getcro(a,b);
    return cross(L.T-L.S,cro-L.S)<-eps;
}
int cnt;
bool Half(){
    head=tail=0;
     for (int i = 0; i < cnt; i++) {
    while(tail - head > 1&&check(tmp[i], que[tail - 1], que[tail - 2])) tail--;
    while(tail - head > 1&&check(tmp[i], que[head], que[head + 1])) head++;
    que[tail++] = tmp[i];
  }
  while(tail - head > 1 && check(que[head], que[tail - 1], que[tail - 2])) tail--;
  while(tail - head > 1 && check(que[tail - 1], que[head], que[head + 1])) head++;
  if (tail - head < 3) return false;
  return true;
}

tutte矩阵


struct Data{
	int pre,nxt;
}row[Maxn];
bool b[Maxn];
void del(int x){
	b[x]=true;
	row[row[x].pre].nxt=row[x].nxt;
	row[row[x].nxt].pre=row[x].pre;
}

bool edge[Maxn][Maxn];
int A[Maxn][Maxn],c[Maxn],d[Maxn];
int num[Maxn][Maxn],lis[Maxn];
int mate[Maxn];
void mv1(){rep(i,1,n)rep(j,1,n)A[i][j]=num[i][j];}
int getrank(){
	int cnt=0;
	rep(i,1,n)rep(j,1,n)A[i][j]=0;
	rep(i,1,n){
		memcpy(c+1,num[i]+1,sizeof(int)*n);
		rep(j,1,n)
			if(c[j]){
				if(!A[j][j]){
					rep(k,j,n)A[j][k]=c[k];
					d[j]=Fast_Pow(c[j],Mod-2);
					cnt++;lis[cnt]=i;
					break;
				}
				int t=mul(c[j],d[j]);
				rep(k,j,n)c[k]=dec(c[k],mul(t,A[j][k]));
			}
	}
	return cnt;
}
void getinv(){
	rep(i,1,n)rep(j,1,n)if(i==j)A[i][j]=1;else A[i][j]=0;
	rep(i,1,n){
		if(!num[i][i]){
			rep(j,i+1,n)
				if(num[j][i]){
					rep(k,i,n)swap(num[i][k],num[j][k]),swap(A[i][k],A[j][k]);
					rep(k,1,i-1)swap(A[i][k],A[j][k]);
					break;
			}
		}
		int t=Fast_Pow(num[i][i],Mod-2);
		rep(j,i+1,n)
			if(num[j][i]){
				int de=mul(num[j][i],t);
				rep(k,i,n){
					num[j][k]=dec(num[j][k],mul(num[i][k],de));
					A[j][k]=dec(A[j][k],mul(A[i][k],de));
				}
				rep(k,1,i-1)A[j][k]=dec(A[j][k],mul(A[i][k],de));
	    }
	}
	per(i,n,1){
		rep(j,i+1,n)
			if(num[i][j]){
				rep(k,1,n)A[i][k]=dec(A[i][k],mul(A[j][k],num[i][j]));
			}
		int t=Fast_Pow(num[i][i],Mod-2);
		rep(j,1,n)A[i][j]=mul(A[i][j],t);
	}
}

void elim(int x,int y){
	int t=Fast_Pow(A[x][y],Mod-2);
	for(int i=row[0].nxt;i<=n;i=row[i].nxt){
		int cur=mul(A[i][y],t);
		for(int j=row[0].nxt;j<=n;j=row[j].nxt)
			A[i][j]=dec(A[i][j],mul(A[x][j],cur));
	}
}

inline void rd(int &x){
	x=0;char ch=getchar();int f=1;
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	x*=f;
}

int main(){
	rd(n);rd(m);N=n;
	int s,e;
	for(int i=1;i<=m;++i){
		rd(s);rd(e);
		if(s>e)swap(s,e);
		int t=mt_rand()%Mod;
		num[s][e]=t;
		num[e][s]=dec(0,t);
		edge[s][e]=edge[e][s]=true;
	}
	N=getrank();mv1();swap(n,N);
	printf("%d\n",n/2);
	rep(i,0,n+1){
		row[i].pre=i-1;row[i].nxt=i+1;
	}
	rep(i,1,n)rep(j,1,n)num[i][j]=A[lis[i]][lis[j]];
	getinv();
	rep(i,1,n)
		if(!b[i]){
			rep(j,i+1,n)
				if(edge[lis[i]][lis[j]]&&A[i][j]&&!b[j]){
					mate[lis[i]]=lis[j];mate[lis[j]]=lis[i];
					del(i);del(j);
					elim(i,j);elim(j,i);
					break;
				}
			}
	rep(i,1,N)printf("%d ",mate[i]);puts("");
	return 0;
}

支配树

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
int n,m;
#define V 50010
#define E 100010
int rt;
int head[V],v[E],nxt[E],tot=0;
inline void add_edge(int s,int e){tot++;v[tot]=e;nxt[tot]=head[s];head[s]=tot;}

int dfk=0;
int sdom[V],seq[V],label[V],fa[V],anc[V],idom[V];
vector<int> G[V],bucket[V];

void dfs(int u){
    sdom[u]=++dfk;
    seq[dfk]=u;
    label[u]=u;
    for(int i=head[u];i;i=nxt[i]){
        if(!sdom[v[i]]){
            dfs(v[i]);
            fa[v[i]]=u;    
        }
        G[v[i]].push_back(u);
    }
}

inline void compress(int u){
    if(anc[anc[u]]){
        compress(anc[u]);
        if(sdom[label[anc[u]]]<sdom[label[u]])label[u]=label[anc[u]];
        anc[u]=anc[anc[u]];
    }
}

inline int eval(int u){
    if(anc[u]){
        compress(u);
        return label[u];
    }
    return u;
}

inline void link(int p,int u){anc[u]=p;}

ll ans[V];
inline void solve(){
    dfs(rt);
    for(int i=dfk;i>=2;--i){
        int u=seq[i];
        for(int j=0;j<G[u].size();++j)sdom[u]=min(sdom[u],sdom[eval(G[u][j])]);
        int t=fa[u];
        link(t,u);
        bucket[seq[sdom[u]]].push_back(u);
        for(int j=0;j<bucket[t].size();++j){
            int to=bucket[t][j];
            int w=eval(to);
            idom[to]=sdom[w]<sdom[to]?w:t;
        }
        bucket[t].clear();
    }
    ans[rt]=rt;idom[rt]=rt;
    for(int i=2;i<=dfk;++i){
        int u=seq[i];
        if(idom[u]!=seq[sdom[u]])idom[u]=idom[idom[u]];
        ans[u]=ans[idom[u]]+u;
    }
}

inline void rd(int &x){
    x=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
}

int main(){
    while(~scanf("%d%d",&n,&m)){
        tot=0;memset(head,0,sizeof(int)*(n+1));rt=n;
        memset(anc,0,sizeof(int)*(n+1));dfk=0;
        memset(sdom,0,sizeof(int)*(n+1));
        memset(ans,0ll,sizeof(ll)*(n+1));
        for(int i=1;i<=n;++i){
            bucket[i].clear();
            G[i].clear();
        }
        int s,e;
        for(int i=1;i<=m;++i){
            rd(s);rd(e);
            add_edge(s,e); 
        }
        solve();
        for(int i=1;i<n;++i)printf("%lld ",ans[i]);
        printf("%lld\n",ans[n]);
    }
    return 0;
}

带花树

int mate[Maxn],link[Maxn],vis[Maxn],fa[Maxn];
int getroot(int x){
	if(fa[x]!=x)fa[x]=getroot(fa[x]);
	return fa[x];
}
int ss[Maxn],T;
int Lca(int a,int b){
	T++;
	while(ss[a]^T){
		if(a){ss[a]=T;a=getroot(link[mate[a]]);}
		swap(a,b);
	}
	return a;
}
int Q[Maxn<<1],hd,tl;
void Flower(int x,int y,int p){
	while(getroot(x)!=p){
		link[x]=y;
		fa[y=mate[x]]=fa[x]=p;
		if(vis[y]==1)vis[Q[tl++]=y]=2;
		x=link[y];
	}
}
bool Match(int x){
	hd=tl=0;
	for(int i=1;i<=n;++i)vis[fa[i]=i]=0;
	Q[tl++]=x;vis[x]=2;
	while(hd<tl){
		int u=Q[hd];hd++;
		for(int i=head[u];i;i=nxt[i])
			if(!vis[v[i]]){
				vis[v[i]]=1;
				link[v[i]]=u;
				if(!mate[v[i]]){
					int node=v[i];
					while(node){
						u=mate[link[node]];
						mate[mate[node]=link[node]]=node;
						node=u;
					}
					return true;
				}else vis[Q[tl++]=mate[v[i]]]=2;
			}else if(vis[v[i]]==2&&getroot(u)!=getroot(v[i])){
				int p=Lca(u,v[i]);
				Flower(u,v[i],p);
				Flower(v[i],u,p);
			}
	}
	return false;
}

Poly

#define rez resize
const int Mod=998244353;
const int I2=499122177;
typedef vector<int> poly;
 
namespace modular{
    int add(int a,int b){return a+b>=Mod?a+b-Mod:a+b;}
    int dec(int a,int b){return a-b<0?a-b+Mod:a-b;}
    int mul(int a,int b){return 1ll*a*b%Mod;}
    inline int Fast_Pow(int a,int b){
        int res=1;
        while(b){
            if(b&1)res=1ll*res*a%Mod;
            a=1ll*a*a%Mod;
            b>>=1;
        }
        return res;
    }
}using namespace modular;
 
namespace QR{
    int w;
    struct Data{
        int a,b;
        Data(){a=b=0;}
        Data(int _a,int _b){a=_a;b=_b;}
        friend Data operator *(Data t1,Data t2){return Data((1ll*t1.a*t2.a+1ll*t1.b*t2.b%Mod*w)%Mod,(1ll*t1.a*t2.b+1ll*t1.b*t2.a)%Mod);}
    };
    int calc(int x){
        if(!x)return x;
        int tmp=Fast_Pow(x,(Mod-1)>>1);
        if(tmp==Mod-1)return -1;
        int a;
        while(true){
            a=1ll*rand()*rand()%Mod;
            w=((1ll*a*a-x)%Mod+Mod)%Mod;
            if(Fast_Pow(w,(Mod-1)>>1)==Mod-1)break;
        }
        Data res=Data(1,0),H=Data(a,1);
        tmp=(Mod+1)>>1;
        while(tmp){
            if(tmp&1)res=res*H;
            H=H*H;
            tmp>>=1;
        }
        return min(res.a,Mod-res.a);
    }
}
 
namespace Poly{
    int invv[24],len,bit,inv[1<<20|5];
    vector<int> rev[24],w[24];
    inline void init_w(int lm=1<<18){
        w[17].rez(1<<17);w[17][0]=1;w[17][1]=Fast_Pow(3,(Mod-1)/lm);
        for(int i=2;i<lm/2;++i)
            w[17][i]=1ll*w[17][i-1]*w[17][1]%Mod;
        for(int i=16;i>=0;--i){
            w[i].rez(1<<i);
            for(int j=0;j<(1<<i);++j)w[i][j]=w[i+1][j<<1];
        }
        inv[0]=inv[1]=1;
        for(int i=2;i<=lm;++i)inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod;
    }
    void init_r(int up){
        len=1;bit=0;
        while(len<up)len<<=1,bit++;
        if(invv[bit])return;
        rev[bit].rez(len);
        for(int i=0;i<len;++i)rev[bit][i]=(rev[bit][i>>1]>>1)|((i&1)<<(bit-1));
        invv[bit]=inv[len];
    }
    inline poly operator +(poly a,poly b){
        int n=b.size();
        if(a.size()<n)a.rez(n);
        for(int i=0;i<n;++i)a[i]=add(a[i],b[i]);
        return a;
    }
    inline poly poly_add(poly a,poly b){
    	int n=b.size();
        if(a.size()<n)a.rez(n);
        for(int i=0;i<n;++i)a[i]=add(a[i],b[i]);
        return a;
	}
    inline poly operator -(poly a,poly b){
        int n=b.size();
        if(a.size()<n)a.rez(n);
        for(int i=0;i<n;++i)a[i]=dec(a[i],b[i]);
        return a;
    }
    inline poly poly_dec(poly a,poly b){
        int n=b.size();
        if(a.size()<n)a.rez(n);
        for(int i=0;i<n;++i)a[i]=dec(a[i],b[i]);
        return a;
    }
    void NTT(poly &A,int type){
        for(int i=0;i<len;++i)
            if(i<rev[bit][i])swap(A[i],A[rev[bit][i]]);
        for(int i=1,t=0;i<len;i<<=1,++t)
            for(int j=0;j<len;j+=i<<1)
                for(int k=0;k<i;++k){
                    int x=A[j+k];
                    int y=1ll*w[t][k]*A[j+k+i]%Mod;
                    A[j+k]=add(x,y);
                    A[j+k+i]=dec(x,y);
                }
        if(type==1)return;
        reverse(++A.begin(),A.end());
        for(int i=0;i<len;++i)A[i]=1ll*A[i]*invv[bit]%Mod;
    }
    poly operator *(poly a,poly b){
        int n=a.size(),m=b.size(),t=n+m-1;
        if(n<=30||m<=30){
            poly c(t);
            for(int i=0;i<n;++i)
                if(a[i])
                for(int j=0;j<m;++j)
                    c[i+j]=(c[i+j]+1ll*a[i]*b[j])%Mod;
            return c;
        }
        init_r(t);
        a.rez(len);NTT(a,1);
        b.rez(len);NTT(b,1);
        for(int i=0;i<len;++i)a[i]=1ll*a[i]*b[i]%Mod;
        return NTT(a,-1),a.rez(t),a;
    }
    poly poly_mul(poly a,poly b){
        int n=a.size(),m=b.size(),t=n+m-1;
        if(n<=30||m<=30){
            poly c(t);
            for(int i=0;i<n;++i)
                if(a[i])
                for(int j=0;j<m;++j)
                    c[i+j]=(c[i+j]+1ll*a[i]*b[j])%Mod;
            return c;
        }
        init_r(t);
        a.rez(len);NTT(a,1);
        b.rez(len);NTT(b,1);
        for(int i=0;i<len;++i)a[i]=1ll*a[i]*b[i]%Mod;
        return NTT(a,-1),a.rez(t),a;
    }
    poly poly_inv(poly a,int K){
        poly b(1,Fast_Pow(a[0],Mod-2)),c;
        for(int i=1,l=2,n=a.size();i<K;i<<=1,l<<=1){
            init_r(l<<1);c.rez(l);
            for(int j=0;j<l;++j)c[j]=j<n?a[j]:0;
            c.rez(len);NTT(c,1);
            b.rez(len);NTT(b,1);
            for(int j=0;j<len;++j)b[j]=1ll*b[j]*(2-1ll*b[j]*c[j]%Mod+Mod)%Mod;
            NTT(b,-1);b.rez(l);
        }
        return b.rez(K),b;
    }
    poly poly_sqrt(poly a,int K){
        poly b(1,QR::calc(a[0])),c,binv;
        for(int i=1,l=2,n=a.size();i<K;i<<=1,l<<=1){
            binv=poly_inv(b,l);
            c.rez(l);
            for(int j=0;j<l;++j)c[j]=j<n?a[j]:0;
            c=c*binv;
            c.rez(l);b.rez(l);
            for(int j=0;j<l;++j)b[j]=1ll*inv[2]*(c[j]+b[j])%Mod;
        }
        return b.rez(K),b;
    }
    poly poly_deriv(poly a){
        int n=a.size();
        for(int i=1;i<n;++i)a[i-1]=1ll*a[i]*i%Mod;
        a.pop_back();
        return a;
    }
    poly poly_integ(poly a){
        int n=a.size();
        a.push_back(0);
        for(int i=n-1;i>=0;--i)a[i+1]=1ll*a[i]*inv[i+1]%Mod;
        a[0]=0;
        return a;
    }
    poly poly_ln(poly a,int K){
        a=poly_deriv(a)*poly_inv(a,K);
        a.rez(K-1);
        return poly_integ(a);
    }
    poly poly_exp(poly a,int K){
        poly b(1,1),c;
        for(int i=1,l=2,n=a.size();i<K;i<<=1,l<<=1){
            c.rez(l);
            for(int j=0;j<l;++j)c[j]=j<n?a[j]:0;
            c[0]=add(c[0],1);
            b=b*(c-poly_ln(b,l));b.rez(l);
        }
        return b.rez(K),b;
    }
    const int B=32;
    const int Bbit=5;
    int F[1<<20|5],A[1<<20|5],L,tim=0;
    poly BitF[23][B],tmp;
    vector<int> H[23][B];
    void cdq(int l,int r){
        if(r-l+1<=64){
            for(int i=l;i<=r;++i){
                A[i]=1ll*A[i]*inv[i]%Mod;
                for(int j=1;j<=r-i;++j)
                    A[i+j]=(A[i+j]+1ll*A[i]*F[j])%Mod;
            }
            return;
        }
        int lenB=(r-l+1)/B;
        int gg=0;while((1<<gg)<lenB)gg++;
        for(int i=1;i<B;++i){
            H[gg][i].rez(lenB<<1);
            for(int j=0;j<lenB*2;++j)H[gg][i][j]=0;
        }
        for(int i=0;i<B;++i){
            if(i){
                init_r(lenB<<1);
                NTT(H[gg][i],-1);
                for(int j=0;j<lenB;++j)A[l+i*lenB+j]=add(A[l+i*lenB+j],H[gg][i][j+lenB]);
            }
            cdq(l+i*lenB,l+(i+1)*lenB-1);
            tmp.rez(lenB<<1);
            for(int j=0;j<lenB;++j)tmp[j]=A[l+i*lenB+j];
            for(int j=lenB;j<lenB*2;++j)tmp[j]=0;
            init_r(lenB<<1);
            NTT(tmp,1);
            for(int j=i+1;j<B;++j){
                for(int k=0;k<lenB*2;++k)
                    H[gg][j][k]=(H[gg][j][k]+1ll*BitF[bit-1][j-i-1][k]*tmp[k])%Mod;
            }
        }
    }
    poly Poly_exp(poly a,int K){
        if(a.size()==1){
            poly c(K);
            c[0]=1;
            return c;
        }
        L=1;tim=0;
        while(L<K)L<<=1,tim++;
        if(a.size()<L)a.rez(L);
        F[0]=0;
        for(int i=1;i<=L-1;++i)F[i]=1ll*a[i]*i%Mod;
        //0~L-1 1~L-1
        int at=L,z=tim;
        while(z>=Bbit){
            at/=B;z-=Bbit;
            for(int i=0;i<B-1;++i){
                BitF[z][i].rez(at*2);
                for(int j=0;j<at*2;++j)BitF[z][i][j]=F[i*at+j];
                init_r(at*2);
                NTT(BitF[z][i],1);
            }
        }
        A[0]=1;
        for(int i=1;i<=L-1;++i)A[i]=0;
        cdq(0,L-1);
        poly c(K);
        for(int i=0;i<K;++i)c[i]=A[i];
        return c;
    }
    poly Div(poly a,poly b){
        int n=a.size()-1,m=b.size()-1;
        reverse(a.begin(),a.end());
        reverse(b.begin(),b.end());
        b=poly_inv(b,n-m+1);
        a=a*b;
        a.rez(n-m+1);
        reverse(a.begin(),a.end());
        return a;
    }
    poly Left(poly a){
        for(int i=0;i<a.size()-1;++i)a[i]=a[i+1];
        a.pop_back();
        return a;
    }
    poly Right(poly a){
        int t=a.size();a.rez(t+1);
        for(int i=a.size()-2;i>=0;--i)a[i+1]=a[i];
        a[0]=0;
        return a; 
    }
    poly poly_pow(poly a,int K,int m){//末项非0 
        int t=a[0],invt=Fast_Pow(t,Mod-2),ft=Fast_Pow(t,m);
        for(int i=0;i<a.size();++i)a[i]=1ll*a[i]*invt%Mod;
        poly b=poly_ln(a,K);
        for(int i=0;i<K;++i)b[i]=1ll*b[i]*m%Mod;
        b=Poly_exp(b,K);
        for(int i=0;i<K;++i)b[i]=1ll*b[i]*ft%Mod;
        return b;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值