强连通分量
来自这里
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
#include
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 "<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"<1) {
//if(!iscut[u])
++ans;//根节点不可能被统计多次
iscut[u]=1;
// cout<<"fa "<>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<
求割边
来自这里
void Tarjan(int u,int fa)
{
dfn[u] = low[u] = ++indx;
stk.push(u);
int flg = 0;
for(int i=0; idfn[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
#include
#include
#include
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]]'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
*/
点双之间的交点是割点
一些相关结论
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没有任何区别。
(1)SG==0,有某单一游戏的SG>1。
(2)SG!=0,有某单一游戏的SG>1。(必胜SJ)
(3)SG==0,无某单一游戏的SG>1。(必胜SJ)
(4)SG!=0,无某单一游戏的SG>1。
对于n个点的标号无根树,其方案数为
( n − 2 ) ! ∏ ( d i − 1 ) ! \frac{(n-2)!}{\prod (d_i-1)!} ∏(di−1)!(n−2)!
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β)