1. 程式人生 > >ACM模板(個人程式碼集整理)(持續更新)

ACM模板(個人程式碼集整理)(持續更新)

所有程式碼已經搬家到了github中。

目錄:

SAM(*)

SA(*)

PAM(*)

樹鏈剖分(*)

01Trie(*)

ACAM(*)

KMP(*)

LCA(*)

主席樹(*)

點分治(*)

kd-Tree(*)

斜率優化DP

最大流Dicnic(*)

最小費用最大流(SPFA)(*)

線段樹(*)

dfs靠譜找環

靠譜找凸包(*)

tarjan縮點+點雙連通(尋割)+邊雙連通(尋橋)

虛樹(*)

圓方樹(*)

FFT(*)

SAM+LCT線上動態維護parent樹:

#include<bits/stdc++.h>
#define N 1200005
#define inf 1000000007
using namespace std;
int mask;char s[3000005];
int Q;
string chars;
void gets(int mask){
    scanf("%s",s);chars=s;
    for(int j=0;j<chars.length();j++){
        mask=(mask*131+j)%chars.length();
        char t=chars[j];
        chars[j]=chars[mask];
        chars[mask]=t;
    }
}
struct Link_Cut_Tree{
    int top,fa[N],c[N][2],val[N],tag[N],q[N];
    inline void add(int x,int y){if(x)tag[x]+=y,val[x]+=y;}
    inline void pushdown(int x){
        int l=c[x][0],r=c[x][1];
        if(tag[x]){
            add(l,tag[x]);add(r,tag[x]);
            tag[x]=0;
        }
    }
    inline bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
    inline void rotate(int x){
        int y=fa[x],z=fa[y],l,r;
        if(c[y][0]==x)l=0;else l=1;r=l^1;
        if(!isroot(y)){if(c[z][0]==y)c[z][0]=x;else c[z][1]=x;}
        fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
        c[y][l]=c[x][r];c[x][r]=y;
    }
    inline void splay(int x){
        int top=1;q[top]=x;
        for(int i=x;!isroot(i);i=fa[i])q[++top]=fa[i];
        for(int i=top;i;i--)pushdown(q[i]);
        while(!isroot(x)){
            int y=fa[x],z=fa[y];
            if(!isroot(y)){
                if((c[z][0]==y)^(c[y][0]==x))rotate(x);
                else rotate(y);
            }rotate(x);
        }
    }
    void access(int x){for(int t=0;x;t=x,x=fa[x])splay(x),c[x][1]=t;}
    inline void link(int x,int y){fa[x]=y;access(y);splay(y);add(y,val[x]);}
    inline void cut(int x){
        access(x);splay(x);add(c[x][0],-val[x]);
        fa[c[x][0]]=0;c[x][0]=0;
    }    
}T;
struct Suffix_AutoMaton{
    int l[N],fa[N],ch[N][26];
    int cnt,last;
    Suffix_AutoMaton(){cnt=1;last=1;}
    void ins(int c){
        int p=last,np=++cnt;last=np;T.val[np]=1;l[np]=l[p]+1;
        for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
        if(!p)fa[np]=1,T.link(np,1);
        else{
            int q=ch[p][c];
            if(l[p]+1==l[q])fa[np]=q,T.link(np,q);
            else{
                int nq=++cnt;l[nq]=l[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[nq]=fa[q];T.link(nq,fa[q]);
                fa[np]=fa[q]=nq;
                T.cut(q);T.link(q,nq);T.link(np,nq);
                for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
            }
        }
    }
    void build(){
        scanf("%s",s);int len=strlen(s);
        for(int i=0;i<len;i++)ins(s[i]-'A');
    }
    int query(){
        gets(mask);
        int p=1;int len=chars.length();
        for(int i=0;i<len;i++)if(!(p=ch[p][chars[i]-'A']))return 0;
        T.splay(p);
        return T.val[p];
    }
    void add(){
        gets(mask);int len=chars.length();
        for(int i=0;i<len;i++)ins(chars[i]-'A');
    }
}sam;
int main(){
    scanf("%d",&Q);sam.build();
    while(Q--){
        scanf("%s",s);
        if(*s=='A')sam.add();
        else{
            int ans=sam.query();
            printf("%d\n",ans);
            mask^=ans;
        }
    }
    return 0;
}

SAM動態求 出現至少k次本質不同子串個數:

#include<bits/stdc++.h>  
using namespace std;  
const int maxn = 25e4+1000;  
char s[maxn];  int len,k,n,m;  char temp[5];  
struct SAM{  
    int last,cnt,nxt[maxn*2][26],fa[maxn*2],l[maxn*2],num[maxn*2];  
    int ans;  
    void init(){  
        last = cnt=1;  
        memset(nxt[1],0,sizeof nxt[1]);  
        fa[1]=l[1]=num[1]=0;  
        ans=0;  
    }  
    int inline newnode(){  
        cnt++;  
        memset(nxt[cnt],0,sizeof nxt[cnt]);  
        fa[cnt]=l[cnt]=num[cnt]=0;  
        return cnt;  
    }  
    void add(int c){  
        int p = last;    int np = newnode();    
        last = np;    l[np] =l[p]+1;    
        while (p&&!nxt[p][c]){    
            nxt[p][c] = np;    
            p = fa[p];    
        }    
        if (!p){ fa[np] =1;}
        else{    
            int q = nxt[p][c];    
            if (l[q]==l[p]+1){fa[np] =q;}
            else{    
                int nq = newnode();    
                memcpy(nxt[nq],nxt[q],sizeof nxt[q]);    
                fa[nq] =fa[q]; num[nq] = num[q];  
                l[nq] = l[p]+1; fa[np] =fa[q] =nq;    
                while (nxt[p][c]==q){    
                    nxt[p][c]=nq;    
                    p = fa[p];    
                }    
            }    
        }  
        int temp = last;  
        while (temp){  
            if (num[temp]>=k){  break;  }  
            num[temp]++;  
            if (num[temp]==k){  
                ans+=l[temp]-l[fa[temp]];  
            }  
            temp = fa[temp];  
        }    
    }  
}sam;  
int main(){  
    while (scanf("%d%d%d",&n,&m,&k)!=EOF){  
        scanf("%s",s);  
        len = strlen(s);  sam.init();  
        for (int i=0;i<len;i++){  sam.add(s[i]-'a');  }  
        while (m--){  
            int flag;  
            scanf("%d",&flag);  
            if (flag==1){  
                scanf("%s",temp);  
                sam.add(temp[0]-'a');  
            }else{  
                printf("%d\n",sam.ans);  
            }  
        }  
    }  
    return 0;  
}  

SAM 字典序第k小串

#include<bits/stdc++.h>  
using namespace std;  
const int maxn = 9e4+1000;  
int len;  char s[maxn];  
int cntA[maxn];  vector<char> ans;  
struct SAM{  
    int last,cnt,nxt[maxn*2][26],fa[maxn*2],l[maxn*2];  
    int rk[maxn*2],num[maxn*2];  
    void init(){  
        last = cnt=1;  
        memset(nxt[1],0,sizeof nxt[1]);  
        fa[1]=l[1] =0;  
    }  
    void add(int c){  
        int p = last;  int np = ++cnt;  
        last = np;  l[np] =l[p]+1;  
        while (p&&!nxt[p][c]){  
            nxt[p][c] = np;  
            p = fa[p];  
        }  
        if (!p){  fa[np] =1;}
        else{  
            int q = nxt[p][c];  
            if (l[q]==l[p]+1){  fa[np] =q; }
            else{  
                int nq = ++cnt;  
                memcpy(nxt[nq],nxt[q],sizeof nxt[q]);  
                fa[nq] =fa[q];  l[nq] = l[p]+1;  
                fa[np] =fa[q] =nq;  
                while (nxt[p][c]==q){  
                    nxt[p][c]=nq;  
                    p = fa[p];  
                }  
            }  
        }  
    }  
    void build (){  
        for (int i=1;i<=cnt;i++){  
            cntA[l[i]]++;  
        }  
        for (int i=1;i<maxn-5;i++){  
            cntA[i]+=cntA[i-1];  
        }  
        for (int i=1;i<=cnt;i++){  
            rk[cntA[l[i]]--] =i;  
        }  
        for (int i=1;i<=cnt;i++){  
            num[i]=1;  
        }  
        for (int i=cnt;i>=1;i--){  
            int x = rk[i];  
            for (int i=0;i<26;i++){  
                if (nxt[x][i]){  
                    num[x]+=num[nxt[x][i]];  
                }  
            }  
        }  
        num[0]=0;  
    }  
    inline void print(){  
        for (char t:ans){  
            printf("%c",t);  
        }  
        printf("\n");  
    }  
    void query(int K){  
        ans.clear();  
        int now=1; int sum=0;  
        while (true){  
            if (sum==K){  
                print();  
                return ;  
            }  
            int c=0;  
            int last =0;  
            while (sum<K){  
                sum+=num[nxt[now][c]];  
                c++;  
                assert(c<27);  
            }  
            c--;  
            sum-=num[nxt[now][c]];  
            now = nxt[now][c];  
            ans.push_back('a'+c);  
            sum++;  
        }  
    }  
}sam;  
int main(){  
    int Q;  
    scanf("%s%d",s,&Q);  
    sam.init();  int le = strlen(s);  
    for (int i=0;i<le;i++){  
        sam.add(s[i]-'a');  
    }  
    sam.build();  
    while (Q--){  
        int x;  
        scanf("%d",&x);  
        sam.query(x);  
    }  
    return 0;  
}  

SA:

SA+Manacher求本質不同迴文串個數

#include<bits/stdc++.h>  
using namespace std;  
#define rank rk  
const int MAX = 2e5+10000;  
char ch[MAX];  
int cntA[MAX],cntB[MAX],A[MAX],B[MAX],tsa[MAX],rank[MAX],SA[MAX],lc[MAX],h[MAX];  
int n,t;  int Cas =1;   
void init(){  
    memset(ch,0,sizeof ch);  
    ch[0]='z'+1;  
}  
void input(){  
    scanf("%s",ch+1);  
    n =  strlen(ch+1);  
    ch[n*2+1]='#';  
    for (int i=n;i>=1;i--){  
        ch[i*2] = ch[i];  
        ch[i*2-1] ='#';  
    }  
    n = n*2+1;  
    ch[n+1]='\0';  
}  
void get_SA(){  
    for (int i=0;i<=10000;i++) cntA[i]=0;  
    for (int i=1;i<=n;i++) cntA[ch[i]]++;  
    for (int i=1;i<=10000;i++) cntA[i]+=cntA[i-1];  
    for (int i=n;i>=1;i--) SA[cntA[ch[i]]--] =i;  
    rank[SA[1]]=1;  
    for (int i=2;i<=n;i++){  
        rank[SA[i]]=rank[SA[i-1]];  
        if (ch[SA[i]]!=ch[SA[i-1]]) rank[SA[i]]++;  
    }  
    for (int step = 1;rank[SA[n]]<n;step<<=1){  
        for (int i=0;i<=n;i++)cntA[i]=cntB[i]=0;  
        for (int i=1;i<=n;i++){  
            cntA[A[i]=rank[i]]++;  
            cntB[B[i]=(i+step<=n)?rank[i+step]:0]++;  
        }  
        for (int i=1;i<=n;i++) cntA[i]+=cntA[i-1],cntB[i]+=cntB[i-1];  
        for (int i=n;i>=1;i--) tsa[cntB[B[i]]--] =i;  
        for (int i=n;i>=1;i--) SA[cntA[A[tsa[i]]]--] = tsa[i];  
        rank[SA[1]]=1;  
        for (int i=2;i<=n;i++){  
            rank[SA[i]]=rank[SA[i-1]];  
            if (A[SA[i]]!=A[SA[i-1]]||B[SA[i]]!=B[SA[i-1]]) rank[SA[i]]++;  
        }  
    }  
}  
void get_Height(){  
    for (int i=1,j=0;i<=n;i++){  
        if (j) j--;  
        while (ch[i+j]==ch[SA[rank[i]-1]+j])j++;  
        h[rank[i]]=j;  
    }  
}  
void Manacher(){  
    lc[1]=1;  int k=1;  
    for (int i=2;i<=n;i++){  
        int p = k+lc[k]-1;  
        if (i<=p){  
            lc[i]=min(lc[2*k-i],p-i+1);  
        }else{  lc[i]=1;  }  
        while (ch[i+lc[i]]==ch[i-lc[i]])lc[i]++;  
        if (i+lc[i]>k+lc[k])k=i;  
    }  
}  
void print(){  
    printf("%s\n",ch+1);  
    for (int i=1;i<=n;i++){  
        printf("%s %d\n",ch+SA[i],lc[SA[i]]);  
    }  
}  
void solve(){  
    get_SA();get_Height();Manacher();  
    print();  
    long long res =0;  int cnt=0;  
    for (int i=2;i<=n;i++){  
        cnt = min(cnt,h[i]);  
        res+=max(0,lc[SA[i]]-min(h[i],cnt));  
        if (lc[SA[i]]>cnt){  
            cnt = lc[SA[i]];  
        }  
    }  
    printf("Case #%d: %I64d\n",Cas++,res/2);  
}  
int main(){  
    scanf("%d",&t);  
    while (t--){  
        init();  
        input();  
        solve();  
    }  
    return 0;  
}  

PAM

求公共迴文串個數。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;
struct PAM{
	int nxt[maxn][26],len[maxn],cnt[maxn],fail[maxn];
	int S[maxn];int last,p,now;
	int newnode(int l){
		memset(nxt[p],0,sizeof nxt[p]);
		cnt[p]=0;len[p]=l;
		return p++;
	}
	void init(){
		p =0;
		newnode(0);newnode(-1);
		last =0;now =0;
		S[now++] =-1;fail[0]=1;
	}
	inline int get_fail(int x){
		int tx =x;
		while (S[now-len[tx]-2]!=S[now-1]) tx = fail[tx];
		return tx;
	} 
	void add(int c){
		S[now++] =c;
		int cur = get_fail(last);
		if (!nxt[cur][c]){
			int tt = newnode(len[cur]+2);
			fail[tt] = nxt[get_fail(fail[cur])][c];
			nxt[cur][c] =tt;
		}
		last = nxt[cur][c];cnt[last]++;
	}
	void count(){
		for (int i=p-1;i>=0;i--){
			cnt[fail[i]]+=cnt[i];
		}
		cnt[0]=cnt[1]=0;
	}

}pam1,pam2;
long long dfs(int u,int v){
	long long res =0;
	for (int i=0;i<26;i++){
		int uu = pam1.nxt[u][i];
		int vv = pam2.nxt[v][i];
		if (uu&&vv){
			res +=1LL*pam1.cnt[uu]*pam2.cnt[vv];
			res+=dfs(uu,vv);
		}
	}
	return res;
}
int T;int Cas=1; int len1,len2;
char s1[maxn],s2[maxn];
int main(){
	scanf("%d",&T);
	while (T--){
		pam1.init();pam2.init();
		scanf("%s%s",s1,s2);
		len1 = strlen(s1);len2 = strlen(s2);
		for (int i=0;i<len1;i++){
			pam1.add(s1[i]-'a');
		}
		for (int i=0;i<len2;i++){
			pam2.add(s2[i]-'a');
		}
		pam1.count();pam2.count();
		printf("Case #%d: %I64d\n",Cas++,dfs(0,0)+dfs(1,1));
	}
	return 0;
}

樹鏈剖分

樹鏈剖分+樹狀陣列:

#include<bits/stdc++.h>  
using namespace std;  
const int INF = 0x3f3f3f3f;  
const int MAXN = 250000+100;  
struct Seg_Tree{  
    int sm[MAXN<<1];  
    inline int lowbit(int _x){return _x&(-_x);}  
    void build (int l,int r){  
        for (int i=l;i<=r;i++){  
            add(i,1);  
        }  
    }  
    void add(int x,int val){  
        while (x<=MAXN){  
            sm[x]+=val;  
            x+=lowbit(x);  
        }  
    }  
    int sum(int x){  
        int res =0;  
        while (x){  
            res+=sm[x];  
            x-=lowbit(x);  
        }  
        return res;  
    }  
    int query_sum(int l,int r){  
        return sum(r)-sum(l-1);  
    }  
}tree;  
int first[MAXN*2];int nxt[MAXN*2];int des[MAXN*2];  
int tpos[MAXN];int deep[MAXN];int top[MAXN];  
int fa[MAXN]; int wson[MAXN];  int size[MAXN];  
int n,q,tot=0,cnt=0;  char s[10];  
inline void add(int _u,int _v){  
    des[++tot] = _v;  
    nxt[tot] = first[_u];  
    first[_u] = tot;  
}  
void input(){  
    scanf("%d",&n);  
    for (int i=1;i<n;i++){  
        int u,v;  scanf("%d%d",&u,&v);  
        add(u,v);  add(v,u);  
    }  
}  
void dfs(int node,int father){  
    deep[node] = deep[father]+1;  
    fa[node] = father;  size[node] =1;  
    for (int t = first[node];t;t = nxt[t]){  
        int v = des[t];  
        if (v==father){  continue;  }  
        dfs(v,node);  
        if (size[v]>size[wson[node]]){  
            wson[node] = v;  
        }  
        size[node]+=size[v];  
    }  
}  
void dfs2(int node,int father,int chain){  
    top[node] = chain;  tpos[node] = ++cnt;  
    if (wson[node]){  
        dfs2(wson[node],node,chain);  
    }  
    for (int t = first[node];t;t = nxt[t]){  
        int v = des[t];  
        if (v==father||v ==wson[node]){  continue;  }  
        dfs2(v,node,v);  
    }  
}  
void init(){  
    dfs(1,0);  dfs2(1,0,1);  
    tree.build(2,n);  
}  
int get_sum(int u,int v){  
    int res =0;  
    while (top[u]!=top[v]){  
        if (deep[top[u]]<deep[top[v]]){  swap(u,v);  }  
        res+= tree.query_sum(tpos[top[u]],tpos[u]);  
        u = fa[top[u]];  
    }  
    if (deep[u]<deep[v]){  swap(u,v); }  
    res += tree.query_sum(tpos[v],tpos[u]);  
    return res;  
}  
void modify(int u,int v){  
    if (fa[u]!=v){  swap(u,v);  }  
    tree.add(tpos[u],-1);  
}  
void solve(){  
    scanf("%d",&q);  
    q+=n-1;  
    while (q--){  
        scanf("%s",s);  
        if (s[0]=='W'){  
            int x;  
            scanf("%d",&x);  
            printf("%d\n",get_sum(1,x));  
        }else{  
            int x,y;  
            scanf("%d%d",&x,&y);  
            modify(x,y);  
        }  
    }  
}  
int main(){  
    input();  
    init();  
    solve();  
    return 0;  
}  

樹鏈剖分+線段樹:

#include<bits/stdc++.h>  
#define N 100005  
#define inf 1000000000  
using namespace std;  
int n,q,a[4*N];  
struct Edge{  int u,v,next;  }G[N];  
int tot=0,head[N];  
int size[100005],wson[100005],fa[100005],d[100005],top[100005];  
int tpos[100005],pre[100005],cnt=0;  
inline void addedge(int u,int v){  
    G[++tot].u=u;G[tot].v=v;G[tot].next=head[u];head[u]=tot;  
    G[++tot].u=v;G[tot].v=u;G[tot].next=head[v];head[v]=tot;  
}  
void dfs1(int u,int f){  
    size[u]=1;  
    for (int i=head[u];i;i=G[i].next){  
        int v=G[i].v;if (v==f)continue;  
        d[v]=d[u]+1;fa[v]=u;  
        dfs1(v,u);  
        size[u]+=size[v];  
        if (size[v]>size[wson[u]])wson[u]=v;  
    }  
}  
void dfs2(int u,int TP){  
    tpos[u]=++cnt;pre[cnt]=u;top[u]=TP;  
    if (wson[u])dfs2(wson[u],TP);  
    for (int i=head[u];i;i=G[i].next){  
        int v=G[i].v;  
        if (v==fa[u]||v==wson[u])continue;  
        dfs2(v,v);  
    }  
}  
int sumv[4*N],maxv[4*N];  
inline void pushup(int o){  
    sumv[o]=sumv[o*2]+sumv[o*2+1];  
    maxv[o]=max(maxv[o*2],maxv[o*2+1]);  
}  
void build(int o,int l,int r){  
    int mid=(l+r)/2;  
    if (l==r){sumv[o]=maxv[o]=a[pre[l]];return;}  
    build(o*2,l,mid);build(o*2+1,mid+1,r);  
    pushup(o);  
}  
void update(int o,int l,int r,int q,int v){  
    int mid=(l+r)/2;  
    if (l==r){sumv[o]=maxv[o]=v;return;}  
    if (q<=mid)update(o*2,l,mid,q,v);  
    else update(o*2+1,mid+1,r,q,v);  
    pushup(o);  
}  
int querysum(int o,int l,int r,int ql,int qr){  
    int mid=(l+r)/2,ans=0;  
    if (ql<=l&&r<=qr)return sumv[o];  
    if (ql<=mid)ans+=querysum(o*2,l,mid,ql,qr);  
    if (qr>mid)ans+=querysum(o*2+1,mid+1,r,ql,qr);  
    pushup(o);  
    return ans;  
}  
int querymax(int o,int l,int r,int ql,int qr){  
    int mid=(l+r)/2,ans=-inf;  
    if (ql<=l&&r<=qr)return maxv[o];  
    if (ql<=mid)ans=max(ans,querymax(o*2,l,mid,ql,qr));  
    if (qr>mid)ans=max(ans,querymax(o*2+1,mid+1,r,ql,qr));  
    pushup(o);  
    return ans;  
}  
int qsum(int u,int v){  
    int ans=0;  
    while (top[u]!=top[v]){  
        if (d[top[u]]<d[top[v]])swap(u,v);  
        ans+=querysum(1,1,n,tpos[top[u]],tpos[u]);  
        u=fa[top[u]];  
    }  
    if (d[u]<d[v])swap(u,v);  
    ans+=querysum(1,1,n,tpos[v],tpos[u]);  
    return ans;  
}  
int qmax(int u,int v){  
    int ans=-inf;  
    while (top[u]!=top[v]){  
        if (d[top[u]]<d[top[v]])swap(u,v);  
        ans=max(ans,querymax(1,1,n,tpos[top[u]],tpos[u]));  
        u=fa[top[u]];  
    }  
    if (d[u]<d[v])swap(u,v);  
    ans=max(ans,querymax(1,1,n,tpos[v],tpos[u]));  
    return ans;  
}  
int main(){  
    memset(head,0,sizeof(head));  
    memset(a,0,sizeof(a));  
    scanf("%d",&n);  
    for (int i=1;i<n;i++){  
        int u,v;  
        scanf("%d%d",&u,&v);  
        addedge(u,v);  
    }  
    for (int i=1;i<=n;i++)scanf("%d",&a[i]);  
    d[1]=1;fa[1]=1;dfs1(1,-1);dfs2(1,1);build(1,1,n);  
    scanf("%d",&q);  
    while (q--){  
        int x,y;  
        char s[10];  
        scanf("%s%d%d",s,&x,&y);  
        if (s[1]=='H')update(1,1,n,tpos[x],y);  
        if (s[1]=='M')printf("%d\n",qmax(x,y));  
        if (s[1]=='S')printf("%d\n",qsum(x,y));  
    }  
    return 0;  
}  


01Trie:

可持久化01Trie+DFS序 子樹上的點抑或最大值:

#include<bits/stdc++.h>  
using namespace std;  
const int MAX = 1e5+100;  
int bas[35];  int nxt[MAX<<5][2];  
int root[MAX];  int sum[MAX<<5];  
int n,q;  vector<int>E[MAX];  
int st[MAX],en[MAX],rk[MAX];  
int a[MAX];  int cnt;  int tot;  
void sheet(){  
    bas[0]=1;  
    for (int i=1;i<=30;i++){  
        bas[i] = bas[i-1]<<1;  
    }  
}  
void init(){  
    for (int i=0;i<=n;i++){  E[i].clear(); }  
    cnt =tot=0;  
    memset(nxt[0],0,sizeof nxt[0]);  
}  
void input(){  
    for (int i=1;i<=n;i++){  scanf("%d",a+i);  }  
    for (int u=2;u<=n;u++){  
        int v;  scanf("%d",&v);  
        E[u].push_back(v);  E[v].push_back(u);  
    }  
}  
void dfs(int node ,int  father ){  
    st[node] = ++tot;  rk[tot] = node;  
    for (int des:E[node]){  
        if(des==father){  continue; }  
        dfs(des,node);  
    }  
    en[node] = tot;  
}  
int create(){  
    cnt++;  
    memset(nxt[cnt],0,sizeof nxt[cnt]);  
    return cnt;  
}  
int insert(int rt,int val){  
    int y = ++cnt;  int x = rt;  int res = y;  
    for (int i=30;i>=0;i--){  
        sum[y] = sum[x]+1;  
        nxt[y][0] = nxt[x][0];  nxt[y][1] = nxt[x][1];  
        int t = val&bas[i];  
        t>>=i;  
        nxt[y][t] = create();  
        y = nxt[y][t];  x = nxt[x][t];  
    }  
    sum[y] = sum[x]+1;  
    return res;  
}  
int query(int l,int r,int val){  
    int res =0; int x = l; int y = r;  
    for (int i=30;i>=0;i--){  
        int t = val&bas[i];  
        t>>=i;  
        if (sum[nxt[y][!t]]-sum[nxt[x][!t]]){  
            y = nxt[y][!t];  x = nxt[x][!t];  
            res+=bas[i];  
        }else{  
            y = nxt[y][t];  x = nxt[x][t];  
        }   
    }  
    return res;  
}  
void solve(){  
    dfs(1,0);  
    for (int i=1;i<=n;i++){  
        root[i] = insert(root[i-1],a[rk[i]]);  
    }  
    while (q--){  
        int nod,x;  
        scanf("%d%d",&nod,&x);  
        printf("%d\n",query(root[st[nod]-1],root[en[nod]],x));  
    }  
}  
int main(){  
    sheet();  
    while (scanf("%d%d",&n,&q)!=EOF){  
        init();  
        input();  
        solve();  
    }   
    return 0;  
}  


01Trie求區間抑或和的最大值

#include<stdio.h>  
#include<cstring>  
#include<algorithm>  
using namespace std;  
const int MAX = 1e6+100;  
int bas[35];  
const int INF = 2147483645;  
struct Trie{  
    int nxt[MAX<<2][2];  int l[MAX<<2];  
    int cnt;  int ansl,ansr,ansv;  
    void init(){  
        cnt =0;  
        memset(nxt[0],0,sizeof (nxt[0]));  
        memset(l,0x3f3f3f3f,sizeof(l));  
        ansv = 0;  
    }  
    int create(){  
        cnt++;  
        memset(nxt[cnt],0,sizeof (nxt[cnt]));  
        return cnt;  
    }  
    void insert(int id,int x){  
        int y = 0;  
        for (int i=30;i>=0;i--){  
            int t = x&bas[i];  
            t>>=i;  
            if (!nxt[y][t]){  
                nxt[y][t] = create();  
            }  
            y = nxt[y][t];  
        }  
        l[y] = min(l[y],id);  
    }  
    void query(int id,int x){  
        int y=0;  int res =0;  
        for (int i=30;i>=0;i--){  
            int t = x&bas[i];  
            t>>=i;  
            if (nxt[y][!t]){  
                y =nxt[y][!t];  
                res+=bas[i];   
            }else{  
                y = nxt[y][t];  
            }  
        }    
        if (res==ansv){  
            if (l[y]<ansl){  
                ansl = l[y];  ansr = id;  
            }  
        }else if (res>ansv){  
            ansv = res;  
            ansl = l[y];  
            ansr = id;  
        }  
    }  
    void print(int id){  
        printf("Case #%d:\n%d %d\n",id,ansl+1,ansr);  
    }  
}trie;  
void init(){  
    bas[0] = 1;  
    for (int i=1;i<=30;i++){  
        bas[i] = bas[i-1]<<1;  
    }  
}  
int main(){  
    init();   int n,Cas;  
    scanf("%d",&Cas);  
    for (int i=1;i<=Cas;i++){  
        trie.init();  trie.insert(0,0);  
        scanf("%d",&n);  
        int sum=0;  
        for (int j=1;j<=n;j++){  
            int ai;  
            scanf("%d",&ai);  sum^=ai;  
            trie.query(j,sum);  trie.insert(j,sum);  
        }  
        trie.print(i);  
    }  
    return 0;  
}  


AC自動機

ACAM單詞統計模板:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+100;
char s[maxn];
int n,T;
struct AC{
	//不要忘記取過某個單詞之後,把標記清了,防止重複統計!!! 
	int nxt[maxn][26],root,cnt,fail[maxn],val[maxn];
	void init(){
		root = cnt=0;
		memset(nxt[0],0,sizeof nxt[0]);
		fail[0]=val[0]=0;
	}
	inline int create(){
		cnt++;
		memset(nxt[cnt],0,sizeof nxt[cnt]);
		fail[cnt] = val[cnt] = 0;
		return cnt;
	}
	void insert(char word[],int len){
		int now = root;
		for (int i=0;i<len;i++){
			int id = word[i]-'a';
			if(!nxt[now][id]){
				nxt[now][id] = create();
			} 
			now = nxt[now][id];
		}
		val[now]++;
	}
	void build(){
		queue<int> Q;
		fail[root] = root;
		Q.push(root);
		while (!Q.empty()){
			int q = Q.front();Q.pop();
			for (int i=0;i<26;i++){
				if (!nxt[q][i])continue;
				int son = nxt[q][i];
				if (q==root){
					fail[son] = root;
				}else{
					fail[son] = fail[q];
					while (fail[son]&&!nxt[fail[son]][i])fail[son] = fail[fail[son]];
					if (nxt[fail[son]][i]){
						fail[son] = nxt[fail[son]][i];
					}
				}
				Q.push(son);
			}
		}
	}
	int query(char word[],int len){
		int now = root;
		int res=0;
		for (int i=0;i<len;i++){
			int id = word[i]-'a';
			while (now&&!nxt[now][id]) now = fail[now];
			if (nxt[now][id]) now = nxt[now][id];
			int temp = now;
			while (temp){
				res+=val[temp];
				//Important
				val[temp]=0;
				//Important
				temp = fail[temp];
			}
		}
		return res;
	} 
}ac;
int main(){
	scanf("%d",&T);
	while (T--){
		ac.init();
		scanf("%d",&n);
		while (n--){
			scanf("%s",s);
			int len = strlen(s);
			ac.insert(s,len);
		}
		ac.build();
		scanf("%s",s);
		int len = strlen(s);
		printf("%d\n",ac.query(s,len));
	}
	return 0;
}

KMP

KMP+DP求最小壓縮表示法(最小迴圈節)

#include<bits/stdc++.h>  
using namespace std;  
#define MAXN 8005  
#define INF 2147483640  
char s[MAXN];  int n;  
int dp[MAXN];  int nxt[MAXN];  
int nums (int a){  
    int ans = 0;  
    while (a)ans++,a/=10;  
    return ans;  
}  
void kmp(char ss[]){  
    memset(nxt,sizeof(nxt),0);  
    int len = strlen(ss+1);  
    for (int i=2;i<=len;i++){  
        nxt[i]=nxt[i-1];  
        while (nxt[i]&&ss[i]!=ss[nxt[i]+1])nxt[i]=nxt[nxt[i]];  
        nxt[i]+=(ss[i]==ss[nxt[i]+1]);   
    }   
}  
int main(){  
    scanf("%s",s+1);  
    n = strlen(s+1);  
    for (int i=1;i<=n;i++){  dp[i]=INF;  }  
    for (int i=0;i<=n;i++){  
        kmp(s+i);  
        for (int j=i+1;j<=n;j++){  
            int temp = j-i-nxt[j-i];  
            int recyTime = (j-i)%temp==0?(j-i)/temp:1;  
            dp[j] = min(dp[j],dp[i]+(j-i)/recyTime+nums(recyTime));  
        }  
    }      
    cout<<dp[n]<<endl;  
    return 0;  
}  

KMP求最小迴圈節 +DP求最小壓縮表示

    #include<bits/stdc++.h>  
    using namespace std;  
    #define MAXN 8005  
    #define INF 2147483640  
    char s[MAXN];  int n;  
    int dp[MAXN];  int nxt[MAXN];  
    int nums (int a){  
        int ans = 0;  
        while (a)ans++,a/=10;  
        return ans;  
    }  
    void kmp(char ss[]){  
        memset(nxt,sizeof(nxt),0);  
        int len = strlen(ss+1);  
        for (int i=2;i<=len;i++){  
            nxt[i]=nxt[i-1];  
            while (nxt[i]&&ss[i]!=ss[nxt[i]+1])nxt[i]=nxt[nxt[i]];  
            nxt[i]+=(ss[i]==ss[nxt[i]+1]);   
        }  
    }  
    int main(){  
        scanf("%s",s+1);  n = strlen(s+1);  
        for (int i=1;i<=n;i++){  dp[i]=INF;  }  
        for (int i=0;i<=n;i++){  
            kmp(s+i);  
            for (int j=i+1;j<=n;j++){  
                int temp = j-i-nxt[j-i];  
                int recyTime = (j-i)%temp==0?(j-i)/temp:1;  
                dp[j] = min(dp[j],dp[i]+(j-i)/recyTime+nums(recyTime));  
            }  
        }      
        cout<<dp[n]<<endl;  
        return 0;  
    }  

LCA

倍增LCA:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5+100;
int first[maxn],nxt[maxn*2],des[maxn*2];
int st[maxn][21];int dep[maxn];
int m,n;int tot=0;int root;
inline int read(){
	int re_ =0; 
	char ch_ = getchar();
	while (ch_<'0'||ch_>'9')ch_ = getchar();
	while (ch_>='0'&&ch_<='9') re_ = re_*10+ch_-'0',ch_ = getchar();
	return re_;
}
inline void addEdge(int x,int y){
	tot++;des[tot] =y;
	nxt[tot] = first[x];first[x] =tot;
}
inline void input(){
	n = read();m = read();
	root=read();
	for (int i=1;i<n;i++){
		int u=read(),v=read();
		addEdge(u,v);addEdge(v,u);
	}
}
void dfs(int node,int father){
	st[node][0] = father;
	dep[node] =dep[father]+1;
	for (int i=1;i<=20;i++){
		st[node][i] =st[st[node][i-1]][i-1];
		if (!st[node][i])break;
	}
	for (int t =first[node];t;t=nxt[t]){
		int v = des[t];
		if (v==father)continue;
		dfs(v,node);
	}
}
int lca(int x,int y){
	if (dep[x]<dep[y])swap(x,y);
	for (int i=20;i>=0;i--){
		if (dep[st[x][i]]>=dep[y]){
			x = st[x][i];
		}
	}
	if (x==y)return x;
	for (int i=20;i>=0;i--){
		if (st[x][i]!=st[y][i]){
			x=st[x][i];y=st[y][i];
		}
	}
	return st[x][0];
}
inline void solve(){
	int x =read(),y=read();
	printf("%d\n",lca(x,y));
}
int main(){
	input();
	dfs(root,0);
	while (m--){solve();}
	return 0;
}

主席樹

樹上主席樹

#include<bits/stdc++.h>
#define l(x) tree[x].L
#define r(x) tree[x].R
using namespace std;
const int maxn = 1e5+1000;
struct Node{int L,R,val;}tree[maxn*40];
int root[maxn];
vector<int> E[maxn];int a[maxn];
int rk[maxn];int pos[maxn];int st[maxn][21];
int dep[maxn];int cnt,m,n;int lastans=0;
void input(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++){
		scanf("%d",a+i);
		rk[i]=i;
	}
	for (int i=1;i<n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		E[u].push_back(v);E[v].push_back(u);
	}
}
bool cmp(int x,int y){return a[x]<a[y];}
int build (int l,int r){
	int k = cnt++;
	tree[k].val =0;
	if (l==r)return k;
	int mid = l+r >>1;
	tree[k].L = build(l,mid);tree[k].R = build (mid+1,r);
	return k;
}
int update(int P,int l,int r,int pos,int del){
	int k = cnt++;
	tree[k].val = tree[P].val+del;
	if (l==r) return k;int mid = l+r >>1;
	if (pos<=mid){
		tree[k].L = update(tree[P].L,l,mid,pos,del);
		tree[k].R = tree[P].R;
	}else{
		tree[k].L = tree[P].L;
		tree[k].R = update(tree[P].R,mid+1,r,pos,del);
	}
	return k;
}
void dfs(int node,int father){
	root[node] = update(root[father],1,n,pos[node],1);
	st[node][0] =father;dep[node] = dep[father]+1;
	for (int i=1;i<=19;i++){
		st[node][i] = st[st[node][i-1]][i-1];
		if (!st[node][i]){break;}
	}
	for (vector<int>::iterator it = E[node].begin();it!=E[node].end();++it){
		int v = *it;
		if (v==father)continue;
		dfs(v,node);
	}
}
void presolve(){
	sort(rk+1,rk+1+n,cmp);
	for (int i=1;i<=n;i++){pos[rk[i]] =i;}
	root[0] = build(1,n);
	dfs(1,0);
}
int lca(int u,int v){
	if (dep[u]<dep[v]){swap(u,v);}
	for (int i=19;i>=0;i--){
		if (dep[st[u][i]]>=dep[v]){
			u = st[u][i];
		}
	}
	if (u==v){return u;}
	for (int i=19;i>=0;i--){
		if (st[u][i]!=st[v][i]){
			u = st[u][i];
			v = st[v][i];
		}
	}
	return st[u][0];
}
int query_kth(int rtx,int rty,int anc,int fanc,int l,int r,int k){
	if (l==r)return l;
	int mid = l+r>>1;
	int temp = tree[l(rtx)].val+tree[l(rty)].val-tree[l(anc)].val-tree[l(fanc)].val;
	if (temp>=k)return query_kth(tree[rtx].L,tree[rty].L,tree[anc].L,tree[fanc].L,l,mid,k);
	else return query_kth(tree[rtx].R,tree[rty].R,tree[anc].R,tree[fanc].R,mid+1,r,k-temp);
}
int query(int x,int y,int k){
	int anc = lca(x,y);
	int tmp=query_kth(root[x],root[y],root[anc],root[st[anc][0]],1,n,k);
	return a[rk[tmp]];
}
void solve(){
	while (m--){
		int x,y,k;
		scanf("%d%d%d",&x,&y,&k);
		x =x^lastans;
		lastans = query(x,y,k);
		printf("%d",lastans);
		if (m){printf("\n");}
	}
}
int main(){
	input();
	presolve();
	solve();
	return 0;
}


主席樹求區間第k值(模板)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
struct Node{int L,R,val;}tree[maxn*500];
int a[maxn];int rk[maxn];int pos[maxn];
int root[maxn];int cnt,m,n,T;
inline void input(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        rk[i]=i;
    }
}
inline void init(){
    memset(root,0,sizeof root);
    cnt =0;
}
int build (int l,int r){
    int k = cnt++;
    tree[k].val =0;
    if (l==r) return k;
    int mid = l+r >>1;
    tree[k].L = build (l,mid);tree[k].R = build (mid+1,r);
    return k; 
}
int update (int P,int l,int r,int ppos,int del){
    int k = cnt++;
    tree[k].val = tree[P].val +del;
    if (l==r) return k;
    int mid = l+r >>1;
    if (ppos<=mid){
        tree[k].L = update(tree[P].L,l,mid,ppos,del);
        tree[k].R = tree[P].R;
    }else{
        tree[k].L = tree[P].L;
        tree[k].R = update(tree[P].R,mid+1,r,ppos,del);
    }
    return k;
}
bool cmp(int x,int y){return a[x]<a[y];}
void presolve(){
    sort(rk+1,rk+1+n,cmp);
    for (int i=1;i<=n;i++){
        pos[rk[i]] =i;
    }
    root[0] = build (1,n);
    for (int i=1;i<=n;i++){
        root[i] = update(root[i-1],1,n,pos[i],1);
    }
}
int query_kth(int lt,int rt,int l,int r,int k){
    if (l==r) return a[rk[l]];
    int mid = l+r >>1;
    if (tree[tree[rt].L].val-tree[tree[lt].L].val>=k) return query_kth(tree[lt].L,tree[rt].L,l,mid,k);
    else return query_kth(tree[lt].R,tree[rt].R,mid+1,r,k+tree[tree[lt].L].val-tree[tree[rt].L].val);
}
void solve(){
    while (m--){
        int l,r,k;
        scanf("%d%d%d",&l,&r,&k);
        printf("%d\n",query_kth(root[l-1],root[r],1,n,k));
    }
}
int main(){
    scanf("%d",&T);
    while (T--){
        init();
        input();
        presolve();
        solve();
    }
    return 0;
}

點分治

求樹上包含所有顏色(節點有顏色)的有向路徑數:

#include<bits/stdc++.h>  
using namespace std;  
const int maxn = 5e4+100;  
const int maxk = 12;  
int first[maxn],nxt[maxn*2],des[maxn*2],tot;  
int a[maxn];  int bas[maxk];  int status[maxn];  
bool vis[maxn];  long long cnt[1100];  
int sz[maxn],ssz[maxn];  int k,n,maxstatus;  
long long ans;  int Min,Minid;  
const int INF = 0x3f3f3f3f;  
void prework(){  
    bas[0]=1;  
    for (int i=1;i<=10;i++){  
        bas[i] =bas[i-1]*2;  
    }  
}  
void init(){  
    tot=ans=0;  
    memset(vis,0,sizeof vis);  
    memset(first,0,sizeof first);  
}  
inline void addEdge(int x_,int y_){  
    tot++;  des[tot] =y_;  
    nxt[tot]=first[x_];  first[x_]=tot;  
}  
void input(){  
    for (int i=1;i<=n;i++){  
        int flag;  
        scanf("%d",&flag);  
        a[i] =bas[flag-1];  
    }  
    for (int i=1;i<n;i++){  
        int u,v;  
        scanf("%d%d",&u,&v);  
        addEdge(u,v);addEdge(v,u);  
    }  
    maxstatus = (1<<k)-1;  
}  
void getSize(int node,int father){  
    sz[node]=1;  ssz[node]=0;  
    for (int t = first[node];t;t=nxt[t]){  
        int v = des[t];  
        if (v==father||vis[v])continue;  
        getSize(v,node);  
        sz[node]+=sz[v];  
        if (sz[v]>ssz[node])ssz[node] = sz[v];  
    }  
}  
void find_root(int node,int father,int root){  
    int val = max(sz[root]-sz[node],ssz[node]);  
    if (val<Min){  
        Min = val;  
        Minid = node;  
    }  
    for (int t = first[node];t;t=nxt[t]){  
        int v = des[t];  
        if (v==father||vis[v])continue;  
        find_root(v,node,root);  
    }  
}  
int getRoot(int node){  
    getSize(node,-1);  
    Min =Minid = INF;  
    find_root(node,-1,node);  
    return Minid;  
}  
void getStatus(int node,int father){  
    status[node] = status[father]|a[node];  
    cnt[status[node]] ++;  
    for (int t = first[node];t;t=nxt[t]){  
        int v = des[t];  
        if (v==father||vis[v])continue;  
        getStatus(v,node);  
    }  
}  
long long calc(int node,int S){  
    memset(cnt,0,sizeof cnt);  
    getStatus(node,0);  
    long long res =0;  
    for (int i=S;i;i = (i-1)&S){  
        if (cnt[i]){  
            for (int x = i;x;x=(x-1)&i){  
                res += cnt[i]*cnt[x^S];  
            }  
            res +=cnt[i]*cnt[0^S];  
        }  
    }  
    return res;  
}  
void solve(int node){  
    int root = getRoot(node);  
    ans+=calc(root,maxstatus);  
    vis[root] =true;  
    for (int t = first[root];t;t=nxt[t]){  
        int v = des[t];  
        if (vis[v])continue;  
        ans -=calc(v,maxstatus);  
        ans-= calc(v,a[root]^maxstatus);  
        solve(v);  
    }  
}  
int main(){  
    prework();  
    while (scanf("%d%d",&n,&k)!=EOF){  
        init();  input();  
        if (k==1){  
            cout<<1LL*n+1LL*n*(n-1)<<endl;  
            continue;   
        }  
        solve(1);  
        cout<<ans<<endl;  
    }  
    return 0;  
}  


求路徑長度<=K的條數 板子:

#include<stdio.h>  
#include<algorithm>  
#include<cstring>  
using namespace std;  
const int MAX = 1e4+100;  
const int INF = 0x3f3f3f3f;  
int first [MAX*2];  int des[MAX*2];  
int len[MAX*2];  int nxt[MAX*2];  
int n,k,tot;  int a[MAX];  int sum[MAX];  
int dp[MAX];  int dis[MAX];  int num,ans;  
bool vis[MAX];  int Sum,Min,Minid;  
void init(){  
    memset(first,0,sizeof first);  
    tot =0;  ans =0;  
    memset(vis,0,sizeof vis);  
}  
inline void add(int x,int y,int z){  
    tot++;  
    des[tot]= y;  len[tot] =z;  
    nxt[tot] = first[x];  first[x] = tot;  
}  
void input(){  
    for (int i=1;i<n;i++){  
        int u,v,w;  
        scanf("%d%d%d",&u,&v,&w);  
        add(u,v,w);  add(v,u,w);  
    }  
}  
void dfs1(int node,int father){  
    sum[node] = 1;  dp[node] = 0;  
    for (int t = first[node];t;t = nxt[t]){  
        int v = des[t];  
        if (v == father||vis[v]){  
            continue;  
        }  
        dfs1(v,node);  
        sum[node] += sum[v];  
        dp[node] = max(dp[node],sum[v]);  
    }  
}  
void dfs2(int node,int father){  
    int temp = max(dp[node],Sum-sum[node]);  
    if (temp<Min){  
        Min = temp;  Minid = node;  
    }  
    for (int t = first[node];t;t = nxt[t]){  
        int v = des[t];  
        if (v==father||vis[v]){  continue;  }  
        dfs2(v,node);  
    }  
}  
int getRoot(int u){  
    dfs1(u,0);  Sum = sum[u];  
    Min = INF;  Minid = -1;  
    dfs2(u,0);  
    return Minid;  
}  
void getDist(int node,int father,int dist){  
    dis[num++] = dist;  
    for (int t = first[node];t;t = nxt[t]){  
        int v =des[t];  
        if (v == father||vis[v]){  continue;  }  
        getDist(v,node,dist+len[t]);  
    }  
}  
int calc (int u,int val){  
    num=0;  int res =0;  
    getDist(u,0,0);  
    sort(dis,dis+num);  
    int i=0;int j=num-1;  
    while (i<j){  
        if (dis[i]+dis[j]+2*val<=k){  
            res+=j-i;  
            i++;  
        }else{  j--;  }  
    }  
    return res;  
}  
void solve(int u){  
    int root = getRoot(u);  
    ans +=calc(root,0);  vis[root] = true;  
    for (int t = first[root];t;t = nxt[t]){  
        int v = des[t];  
        if (vis[v]){  
            continue;  
        }  
        ans-=calc(v,len[t]);  
        solve(v);  
    }  
}  
int main(){  
    while (scanf("%d%d",&n,&k)!=EOF&&n&&k){  
        init();  
        input();  
        solve(1);  
        printf("%d\n",ans);  
    }  
    return 0;  
}   

kd-Tree

二維樹且第三維有限制的搜尋模板:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+100;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
int m,n;
const int demension = 2;
struct Hotel{
	int pos[demension],id,c;
}hotel[maxn],kdtree[maxn];
double var[demension];
int split [maxn];int cmpDem;
bool cmp(const Hotel &a,const Hotel &b){
	return a.pos[cmpDem]<b.pos[cmpDem];
}
void build (int l,int r){
	if (l>=r)return;
	int mid = l+r >>1;
	for (int i=0;i<demension;i++){
		double ave =0;
		for (int j=l;j<=r;j++){
			ave+=hotel[j].pos[i];
		}
		ave/=(r-l+1);var[i] =0;
		for (int j=l;j<=r;j++){
			var[i]+=pow(hotel[j].pos[i]-ave,2);
		}
		var[i]/=(r-l+1);
	}
	split[mid] =-1;double maxVar=-1;
	for (int i=0;i<demension;i++){
		if (var[i]>maxVar){
			maxVar = var[i];
			split[mid] =i;
		}
	}
	cmpDem = split[mid];
	nth_element(hotel+l,hotel+mid,hotel+r+1,cmp);
	build (l,mid-1);build (mid+1,r);
}
int ansIndex;
LL ansDis;
void query(int l,int r,const Hotel& x){
	if (l>r)return ;
	int mid = l+r >>1;LL dis =0;
	for (int i=0;i<demension;i++){
		dis +=1LL*(x.pos[i]-hotel[mid].pos[i])*(x.pos[i]-hotel[mid].pos[i]);
	}
	if (hotel[mid].c<=x.c){
		if (ansDis == dis && hotel[mid].id<hotel[ansIndex].id){
			ansIndex = mid;
		}else if (dis<ansDis){
			ansDis = dis;
			ansIndex = mid;
		} 
	}
	int d = split[mid];
	LL radius = 1LL*(x.pos[d]-hotel[mid].pos[d])*(x.pos[d]-hotel[mid].pos[d]);
	if (x.pos[d]<hotel[mid].pos[d]){
		query(l,mid-1,x);
		if (ansDis>radius){query(mid+1,r,x);}
	}else{
		query(mid+1,r,x);
		if (ansDis>radius){query(l,mid-1,x);}
	}
}
int T;
void input(){
	scanf("%d%d",&n,&m);
	for (int i=0;i<n;i++){
		scanf("%d%d%d",&hotel[i].pos[0],&hotel[i].pos[1],&hotel[i].c);
		hotel[i].id=i;
	}
	build (0,n-1);
}
void solve(){
	Hotel x;
	for (int i=1;i<=m;i++){
		scanf("%d%d%d",&x.pos[0],&x.pos[1],&x.c);
		ansDis = INF;ansIndex =n+1;
		query(0,n-1,x);
		printf("%d %d %d\n",hotel[ansIndex].pos[0],hotel[ansIndex].pos[1],hotel[ansIndex].c);
	}
}
int main(){
	scanf("%d",&T);
	while (T--){
		input();
		solve();
	}
	return 0;
}

斜率優化DP:

裸斜率優化DP板子,方程dp[ i ] = min { dp[ j ] + ( sum[ i ]- sum[ j ] )^2 }

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 5e5+10;
ll dp[maxn],q[maxn],sum[maxn];
ll y(int j,int k)
{return dp[j]+sum[j]*sum[j]-dp[k]-sum[k]*sum[k];}
ll x(int j,int k)
{return 2*(sum[j]-sum[k]);}
ll getdp(int i,int j)
{return dp[j]+(sum[i]-sum[j])*(sum[i]-sum[j]);}
int main()
{
    ll n,m;
    while(scanf("%I64d %I64d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;i++)
            scanf("%I64d",&sum[i]);
        sum[0]=dp[0]=0;
        for(int i=1;i<=n;i++)
            sum[i]+=sum[i-1];
        int head=0,tail=0;
        q[tail++]=0;
        for(int i=1;i<=n;i++)
        {
            while(head+1<tail&&y(q[head+1],q[head])<=sum[i]*x(q[head+1],q[head]))
                head++;
            dp[i]=getdp(i,q[head])+m;
            while(head+1<tail&&y(i,q[tail-1])*x(q[tail-1],q[tail-2])<=y(q[tail-1],q[tail-2])*x(i,q[tail-1]))
                tail--;
            q[tail++]=i;
        }
        printf("%I64d\n",dp[n]);
    }
    return 0;
}

樹上斜率DP,重點在於恢復佇列元素,方程:dp[ i ] = min{ dp[ j ] + ( sum[ i ]- sum[ j ] )^2 } +p

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+100;
typedef long long LL ;
int first[maxn],nxt[maxn*2],des[maxn*2],len[maxn*2],tot;
LL dp[maxn],sum[maxn];
int q[maxn],l,r;int T;
int n,p;
struct Node{
    int pos,val;
    Node (int pos_,int val_):pos(pos_),val(val_){}
};
inline LL x(int k,int j){
    return 2LL*(sum[k]-sum[j]);
} 
inline LL y(int k,int j){
    return dp[k]+sum[k]*sum[k]-dp[j]-sum[j]*sum[j];
}
inline LL getdp(int i,int j){
    return dp[j]+(sum[i]-sum[j])*(sum[i]-sum[j])+p;
}
inline void addEdge(int x,int y,int z){
    tot++;
    des[tot] =y;len[tot] = z;
    nxt[tot] = first[x];first[x] =tot;
}
void init(){
    memset(first,0,sizeof first);
    tot =0;
}
void input(){
    scanf("%d%d",&n,&p);
    for (int i=0;i<n-1;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        addEdge(u,v,w);addEdge(v,u,w);
    }
}
void dfs(int node,int father){
    vector<Node> q2;
    while (l+1<r&&y(q[l],q[l+1])>=sum[node]*x(q[l],q[l+1])){
        q2.emplace_back(Node(l,q[l]));
        l++;
    }
    if (node!=1) dp[node] =  getdp(node,q[l]);
    while (l+1<r&&y(q[r-2],q[r-1])*x(q[r-1],node)>=y(q[r-1],node)*x(q[r-2],q[r-1])){
        q2.emplace_back(Node(r-1,q[r-1]));
        r--;
    }
    q[r++] = node;
    int nowl = l,nowr = r;
    for (int t = first[node];t;t=nxt[t]){
        int v = des[t];int w = len[t];
        if (v==father)continue;
        l = nowl;r = nowr;
        sum[v] = sum[node]+w;
        dfs(v,node);
    }
    for (Node temp : q2){
        q[temp.pos] = temp.val;
    }
}
void solve(){
    dp[1] = -p;
    l =r =0;
    dfs(1,0);
    LL ans =0;
    for (int i=1;i<=n;i++){ans = max(ans,dp[i]);}
    cout<<ans<<endl;
}
int main(){
    scanf("%d",&T);
    while (T--){
        init();
        input();
        solve();
    }
    return 0;
}

最大流

Dicnic模板

#include<bits/stdc++.h>
using namespace std;
const int maxn = 205;
const int INF = 0x3f3f3f3f;
int first[maxn],nxt[maxn*2],des[maxn*2],c[maxn*2],tot;
int dep[maxn];int m,n,ss,tt;
void init(){
	memset(first,-1,sizeof first);
	tot =-1;
}
inline void addEdge(int u,int v,int w){
	tot++;
	des[tot] = v;c[tot] =w;
	nxt[tot] = first[u];first[u] = tot;
}
void input(){
	for (int i=0;i<m;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		addEdge(u,v,w);addEdge(v,u,0);
	}
}
bool bfs(){
	memset(dep,-1,sizeof dep);
	dep[ss] =0;
	queue<int> Q;Q.push(ss);
	while (!Q.empty()){
		int q = Q.front();Q.pop();
		for (int t = first[q];t!=-1;t= nxt[t]){
			int v = des[t],cx = c[t];
			if (dep[v]==-1&&cx){
				dep[v] = dep[q]+1;
				Q.push(v);
			}
		}
	}
	return dep[tt]!=-1;
}
int dfs(int node,int now){
	if (node==tt)return now;
	int res =0;
	for (int t = first[node];t!=-1&&res<now;t=nxt[t]){
		int v = des[t],cx = c[t];
		if (dep[v]==dep[node]+1&&cx){
			int x = min(cx,now-res);
			x = dfs(v,x);
			res +=x;
			c[t] -= x;c[t^1]+=x;
		}
	}
	if (!res) dep[node] = -2;
	return res;
}
void solve(){
	int res =0,del =0;
	ss =1;tt =n;
	while (bfs()){
		while (del = dfs(ss,INF)){res +=del;}
	}
	cout<<res<<endl;
}
int main(){
	while (scanf("%d%d",&m,&n)!=EOF){
		init();
		input();
		solve();
	}
	return 0;
}

最小費用最大流

來回最短路&雙向邊&每個方向的邊只能用一次(模板)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 2000+50;
const int maxm = 20000+50;
const int INF = 0x3f3f3f3f;
int m,n;
int first[maxn],from[maxm*2],des[maxm*2],nxt[maxm*2],cost[maxm*2],flow[maxm*2],tot;
int dis[maxn],pre[maxn];
bool in[maxn];int ss,tt;
inline void addE(int x,int y,int f,int c){
	tot++;
	from[tot] =x;des[tot] =y;
	flow[tot] =f;cost[tot] =c;
	nxt[tot] = first[x];first[x] = tot;
}
inline void addEdge(int x,int y,int f,int c){
	addE(x,y,f,c);addE(y,x,0,-c);
}
void input(){
	scanf("%d%d",&n,&m);
	tot =-1;
	memset(first,-1,sizeof first);
	for (int i=0;i<m;i++){
		int u,v,c;
		scanf("%d%d%d",&u,&v,&c);
		addEdge(u,v,1,c);addEdge(v,u,1,c);
	}
	addEdge(0,1,2,0);
}
bool spfa(){
	memset(in,0,sizeof in);
	memset(dis,INF,sizeof dis);
	memset(pre,-1,sizeof pre);
	dis[ss] =0;in[ss] =1;
        queue<int> Q;Q.push(ss);
	while (!Q.empty()){
		int q = Q.front();
		Q.pop();
		in[q] = 0;
		for (int t = first[q];t!=-1;t = nxt[t]){
			int v = des[t];
			int len = cost[t];
			int cx = flow[t];
			if (cx&&dis[v]>dis[q]+len){
				dis[v] = dis[q]+len;
				pre[v] = t;
				if (!in[v]){
					Q.push(v);in[v] = 1;
				}
			}
		}
	}
	return pre[tt]!=-1;
}
void solve(){
	ss =0;tt=n;
	int totflow =0,totcost =0,nowflow =0,nowcost =0;
	while (spfa()){
		nowcost =0;
		nowflow = INF;
		int now =pre[tt];
		while (now!=-1){
			nowflow = min(nowflow,flow[now]);
			now = pre[from[now]];
		}
		now = pre[tt];
		while (now!=-1){
			flow[now] -= nowflow;
			flow[now^1] += nowflow;
			nowcost +=cost[now];
			now = pre[from[now]];
		} 
		nowcost*=nowflow;
		totflow +=nowflow;
		totcost +=nowcost;
	}
	cout<<totcost<<endl;
}
int main(){
	input();
	solve();
	return 0;
}

線段樹

區間修改,區間求和

#include<stdio.h>
using namespace std;
const int maxn = 1e5+100;
typedef long long LL;
int a[maxn];
struct Seg_Tree{
	LL val[maxn*4];LL lazy[maxn*4];
	inline void Up(int x){val[x] = val[x<<1]+val[x<<1|1];}
	inline void Down(int x,int l,int mid,int r){
		if (lazy[x]){
			val[x<<1] += 1LL*lazy[x]*(mid-l+1);
			val[x<<1|1] += 1LL*lazy[x]*(r-mid);
			lazy[x<<1]+= lazy[x];
			lazy[x<<1|1] += lazy[x];
			lazy[x] =0;
		}
	}
	void build (int x,int l,int r){
		lazy[x] =0;
		if (l==r){val[x] = a[l];return ;}
		int mid = l+r >>1;
		build (x<<1,l,mid);build (x<<1|1,mid+1,r);
		Up(x);
	}
	void add(int x,int l,int r,int L,int R,int del){
		if (l>R||r<L)return;
		if (L<=l&&r<=R){
			val[x]+=1LL*del*(r-l+1);
			lazy[x]+=del;
			return;
		}
		int mid = l+r >>1;
		Down(x,l,mid,r);
		add(x<<1,l,mid,L,R,del);add(x<<1|1,mid+1,r,L,R,del);
		Up(x);
	}
	LL query_Sum(int x,int l,int r,int L,int R){
		if (l>R||r<L)return 0;
		if (L<=l&&r<=R)return val[x];
		int mid = l+r >>1;
		Down(x,l,mid,r);
		return query_Sum(x<<1,l,mid,L,R)+query_Sum(x<<1|1,mid+1,r,L,R);
	}
}tree;
char opt[5];
int m,n;
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++){
		scanf("%d",a+i);
	}
	tree.build(1,1,n);
	while (m--){
		int l,r,v;
		scanf("%s%d%d",opt,&l,&r);
		if (opt[0]=='Q'){
			printf("%I64d\n",tree.query_Sum(1,1,n,l,r));
		}else if (opt[0]=='C'){
			scanf("%d",&v);
			tree.add(1,1,n,l,r,v);
		}
	}
	return 0;
}

二位線段樹,單點修改,二維最大值查詢

    #include<bits/stdc+ 
    #define LL long long  
    #define inf 1<<30  
    #define s(a) scanf("%d",&a)  
    #define CL(a,b) memset(a,b,sizeof(a))  
    using namespace std;  
    const int N=5005;  
    int n,a,b;  
    float seg[N][N<<2];     //  表示X軸Y軸;  
    void Build_2(int l,int r,int deep,int rt)       //  建y軸方向線段樹(二維);  
    {  
        seg[deep][rt]=-1.0;  
        if(l==r) return;  
        int mid=(l+r)>>1;  
        Build_2(l,mid,deep,rt<<1);  
        Build_2(mid+1,r,deep,rt<<1|1);  
    }  
    void Build(int l,int r,int rt)      //  建x軸方向線段樹(一維);  
    {  
        Build_2(0,1000,rt,1);  
        if(l==r) return;  
        int mid=(l+r)>>1;  
        Build(l,mid,rt<<1);  
        Build(mid+1,r,rt<<1|1);  
    }  
    void Insert_2(int act,float love,int l,int r,int deep,int rt)  //  y軸方向更新資料;(二維)  
    {  
        seg[deep][rt]=max(love,seg[deep][rt]);  
        if(l==r) return;  
        int mid=(l+r)>>1;  
        if(act<=mid) Insert_2(act,love,l,mid,deep,rt<<1);  
        else Insert_2(act,love,mid+1,r,deep,rt<<1|1);  
        seg[deep][rt]=max(seg[deep][rt<<1],seg[deep][rt<<1|1]);  
    }  
    void Insert(int h,int act,float love,int l,int r,int rt)       //  x軸,一維;  
    {  
        Insert_2(act,love,0,1000,rt,1);  
        if(l==r) return;  
        int mid=(l+r)>>1;  
        if(h<=mid) Insert(h,act,love,l,mid,rt<<1);  
        else Insert(h,act,love,mid+1,r,rt<<1|1);  
    }  
    float Query_2(int L,int R,int l,int r,int rt,int deep)     //  查詢,y軸,二維;  
    {  
        if(L<=l&&R>=r) return seg[deep][rt];  
        int mid=(l+r)>>1;  
        if(R<=mid) return Query_2(L,R,l,mid,rt<<1,deep);  
        else if(L>mid) return Query_2(L,R,mid+1,r,rt<<1|1,deep);  
        else return max(Query_2(L,R,l,mid,rt<<1,deep),Query_2(L,R,mid+1,r,rt<<1|1,deep));  
    }  
    float Query(int h1,int h2,int L,int R,int l,int r,int rt)      //  x軸,一維;  
    {  
        if(h1<=l&&h2>=r) return Query_2(L,R,0,1000,1,rt);  
        int mid=(l+r)>>1;  
        if(h2<=mid) return Query(h1,h2,L,R,l,mid,rt<<1);  
        else if(h1>mid) return Query(h1,h2,L,R,mid+1,r,rt<<1|1);  
        else return max(Query(h1,h2,L,R,l,mid,rt<<1),Query(h1,h2,L,R,mid+1,r,rt<<1|1));  
    }  
    int main(){  
        int n;  
        while(~scanf("%d",&n)&&n){  
            Build(100,200,1);  
            char ch[5];  
            while(n--){  
                scanf("%s",&ch);  
                if(ch[0]=='I'){  
                    int h;  
                    float x,y;  
                    scanf("%d%f%f",&h,&x,&y);  
                    Insert(h,(int)(x*10),y,100,200,1);  
                }else{  
                    int h1,h2;  float x1,x2;  
                    scanf("%d%d%f%f",&h1,&h2,&x1,&x2);  
                    if(h1>h2){ int temp=h1; h1=h2;h2=temp; }  
                    if(x1>x2){ float temp=x1;x1=x2;x2=temp; }  
                    float ans=Query(h1,h2,(int)(x1*10),(int)(x2*10),100,200,1);  
                    if(ans==-1.0) printf("-1\n");  
                    else printf("%.1lf\n",ans);  
                }  
            }  
        }  
        return 0;  
    }  

DFS靠譜找環

題目:有向圖中最多刪掉一條邊,是否可以成為DAG

#include<bits/stdc++.h>
using namespace std;
const int maxn = 505;
const int maxm = 100005;
int first[maxn],nxt[maxm],des[maxm],tot;
int vis[maxn];
int m,n;vector<int> cir;
stack<pair<int,int> > stk;
inline void addEdge(int u,int v){
    tot++;des[tot] =v;
    nxt[tot] = first[u];first[u] = tot;
}
void input(){
    scanf("%d%d",&n,&m);
    for (int i=0;i<m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        addEdge(u,v);
    }
}
int findcir(int node,int Eid){
    if (vis[node]==2)return 0;
    if (vis[node]==1){
        cir.push_back(Eid);
        while (stk.top().first!=node){
            cir.push_back(stk.top().second);
            stk.pop();
        }
        return 1;
    }
    vis[node]=1;
    stk.push(make_pair(node,Eid));
    for (int t = first[node];t;t=nxt[t]){if (findcir(des[t],t))return 1;}
    vis[node] =2;stk.pop();
    return 0;
}
int findcir2(int node,int Eid){
    if (vis[node]==2)return 0;
    if (vis[node]==1)return 1;
    vis[node]=1;
    for (int t = first[node];t;t=nxt[t]){
        if (t==Eid)continue;
        if (findcir2(des[t],Eid))return 1;
    }
    vis[node]=2;
    return 0;
}
int check(int Eid){
    memset(vis,0,sizeof vis);
    for (int i=1;i<=n;i++)if (!vis[i])if (findcir2(i,Eid))return 0;
    return 1;
}
void solve(){
    memset(vis,0,sizeof vis);
    for (int i=1;i<=n;i++)if (!vis[i])if (findcir(i,-1))break;
    if (cir.empty()){
        cout<<"YES"<<endl;
        return ;
    }
    for (int Eid:cir){
        if (check(Eid)){
            cout<<"YES"<<endl;
            return ;
        }
    }
    cout<<"NO"<<endl;
}
int main(){
    input();
    solve();
    return 0;
}


靠譜找凸包(dis注意精度,注意n=1和n=2特判)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1005;
#define M_PI 3.1415926535
struct Node{int x,y;};
int st[maxn],top; Node a[maxn];
int rk[maxn];int n,T,l;
LL cross(const Node &a,const Node &b,const Node &c){
	return 1LL*(b.x-a.x)*(c.y-a.y)-1LL*(c.x-a.x)*(b.y-a.y);
}
LL cross(int x,int y,int z){return cross(a[x],a[y],a[z]);}
double dis(const Node &a,const Node &b){
	return sqrt(1.0*(a.x-b.x)*(a.x-b.x)+1.0*(a.y-b.y)*(a.y-b.y));
}
bool cmp(int x,int y){
	LL m = cross(a[rk[0]],a[x],a[y]);
	if (m>0)return 1;
	else if (m==0&&dis(a[rk[0]],a[x])<=dis(a[rk[0]],a[y]))return 1;
	else return 0;
}
void solve(){
	scanf("%d%d",&n,&l);
	for (int i=0;i<n;i++){
		scanf("%d%d",&a[i].x,&a[i].y);
		rk[i]=i;
	}
	for (int i=1;i<n;i++){
		if (a[rk[i]].y<a[rk[0]].y||a[rk[i]].y==a[rk[0]].y&&a[rk[i]].x<a[rk[0]].x)swap(rk[i],rk[0]);
	}
	sort(rk+1,rk+n,cmp);top=2;
	st[0]=rk[0];st[1]=rk[1];
	for (int i=2;i<n;i++){
		while (cross(st[top-2],st[top-1],rk[i])<0)top--;
		st[top++] =rk[i];
	}
	double ans =0;
	for (int i=1;i<top;i++){ans+=dis(a[st[i]],a[st[i-1]]);}
	ans+=dis(a[st[0]],a[st[top-1]]);
	ans+=2*M_PI*l;
	printf("%.0lf\n",ans);
}
int main(){
	scanf("%d",&T);
	while (T--){
		solve();
		if (T!=0)printf("\n");
	}
	return 0;
}

tarjan縮點(粗體是tarjan部分,flag是並查集)

縮點+拓撲找出度為零的最小點集
 

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+100;
int m,n,h;int t[maxn];
int first[maxn*2],nxt[maxn*2],des[maxn*2],tot;
int dfn[maxn],low[maxn],dft;bool d[maxn];
int flag[maxn],cnt[maxn],scc;stack<int> stk;
inline void add(int x,int y){
    tot++;des[tot] =y;
    nxt[tot] = first[x];first[x] =tot;
}
void tar(int node){
    dfn[node] = low[node] = ++dft;
    stk.push(node);
    for (int t = first[node];t;t=nxt[t]){
        int v = des[t];
        if (!dfn[v])tar(v);
        low[node] = min(low[node],low[v]);    
    }
    if (dfn[node]==low[node]){
        scc++;
        while (true){
            int temp = stk.top();
            flag[temp]=scc;
            cnt[scc]++;stk.pop();
            if (temp==node)break;
        }
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&h);
    for (int i=1;i<=n;i++){scanf("%d",t+i);}
    for (int i=0;i<m;i++){
        int u1,u2;
        scanf("%d%d",&u1,&u2);
        if (t[u1]==(t[u2]+1)%h)add(u2,u1);
        if (t[u2]==(t[u1]+1)%h)add(u1,u2);
    }
    for (int i=1;i<=n;i++){if (!dfn[i])tar(i);}
    for (int i=1;i<=n;i++){
        for (int t = first[i];t;t=nxt[t]){
            if (flag[i]==flag[des[t]])continue;
            else{d[flag[i]]++;}
        } 
    }
    cnt[0] =n+1;int ans = 0;
    for (int i=1;i<=scc;i++){
        if (d[i]==0&&cnt[i]<cnt[ans]){ans = i;}
    }
    cout<<cnt[ans]<<endl;
    for (int i=1;i<=n;i++){
        if (flag[i]==ans){cout<<i<<" ";}
    }
    cout<<endl;
    return 0;
    
}

Tarjan邊雙連通

統計聯通分量點數和邊數

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+100;
int first[maxn],nxt[maxn*2],from[maxn*2],des[maxn*2],isBrige[maxn*2],tot;
int dfn[maxn],low[maxn],dfs_clock;
int cnt_e[maxn],cnt_n[maxn];int bcc_cnt;
bool ok[maxn];vector <int> ans;int m,n;
inline void addEdge(int x,int y){
	tot++;
	des[tot] =y;from[tot] =x;
	nxt[tot] = first[x];first[x] = tot;
}
void input(){
	cin>>n>>m;
	for (int i=0;i<m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		addEdge(u,v);addEdge(v,u);
	}
}
void dfs(int u,int fa){
	dfn[u] = low[u] = ++dfs_clock;
	for (int t = first[u];t;t=nxt[t]){
		int v = des[t];if (v==fa)continue;
		if (!dfn[v]){
			dfs(v,u);
			low[u] = min(low[v],low[u]);
			if (dfn[u]<low[v]){
				isBrige[t] = true;
				if (t&1){isBrige[t+1] = true;}
                                else{isBrige[t-1] = true;}
			}
		}else if (dfn[v]<dfn[u]){low[u] = min(low[u],dfn[v]);}
	}
}
void blood_fill(int x){
	dfn[x] = bcc_cnt;
	for (int t = first[x];t;t=nxt[t]){
		if (isBrige[t])continue;
		int v = des[t];
		if (!dfn[v]){blood_fill(v);}
	}
}
void check(){
	for (int i=1;i<=n;i++){cnt_n[dfn[i]]++;}
	for (int i=1;i<=tot;i++){
		if (isBrige[i]) continue;
		cnt_e[dfn[des[i]]]++;
	}
	for (int i=1;i<=bcc_cnt;i++){
		if (cnt_n[i]*2==cnt_e[i]){ok[i]=1;}
	}
}
void output(){
	for (int i=1;i<=tot;i+=2){
		if (isBrige[i])continue;
		if (ok[dfn[des[i]]])ans.push_back((i+1)/2);
	}
	sort(ans.begin(),ans.end());
	cout<<ans.size()<<endl;
	for (int i=0;i<ans.size();i++){printf("%d ",ans[i]);}	
}
void solve(){
	for (int i=1;i<=n;i++){if (!dfn[i])dfs(i,-1);}
	memset(dfn,0,sizeof dfn);
	for (int i=1;i<=n;i++){
		if (!dfn[i]){
			bcc_cnt++;
			blood_fill(i);
		}
	}
	check();output();
}
int main(){
	input();
	solve();
	return 0;
} 

Tarjan點雙連通

連通分量中點數=邊數=>整個分量為無向圖中一個獨立的簡單環。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+100;
int first[maxn],des[maxn*2],nxt[maxn*2],tot;
int bcc_cnt,cnt_n[maxn],cnt_e[maxn],bcc_no[maxn];
int dfn[maxn],low[maxn],dfs_clock;
int st[maxn*2],top;bool ok[maxn];
vector<int> ans;vector<int> temp;
int m,n;
inline void addEdge(int x,int y){
	tot++;des[tot] = y;
	nxt[tot] = first[x];first[x] = tot;
}
void input(){
	cin>>n>>m;
	for (int i=0;i<m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		addEdge(u,v);addEdge(v,u);
	}
}
void dfs(int u,int fa){
	dfn[u] = low[u] = ++dfs_clock;
	for (int t = first[u];t;t=nxt[t]){
		int v = des[t];
		if (v==fa)continue;
		if (!dfn[v]){
			st[top++] = t;dfs(v,u);
			low[u] = min(low[u],low[v]);
			if (low[v]>=dfn[u]){
				bcc_cnt++;ok[bcc_cnt] = true;
				temp.clear();
				while (true){
					int tt = st[--top];
					temp.push_back((tt+1)/2);
					if (bcc_no[des[tt]]!=bcc_cnt){
						bcc_no[des[tt]] = bcc_cnt;
						cnt_n[bcc_cnt]++;
					}else{
						ok[bcc_cnt] = false;
					}
					cnt_e[bcc_cnt]++;
					if (tt==t){
						break;
					}
				}
				if (ok[bcc_cnt]&&temp.size()>1){
					for (int i=0;i<temp.size();i++){
						ans.push_back(temp[i]);
					}
				} 
			}
		}else if (dfn[v]<dfn[u]){
			st[top++] = t;
			low[u] = min(low[u],dfn[v]);
		}
	}
}
void solve(){
	for (int i=1;i<=n;i++){if (!dfn[i])dfs(i,-1);}
	sort(ans.begin(),ans.end());
	cout<<ans.size()<<endl;
	for (int i=0;i<ans.size();i++){printf("%d ",ans[i]);}
}
int main(){
	input();
	solve();
	return 0;
}

虛樹

給出有根帶邊權樹,每次詢問:刪除邊使得根與詢問點集不聯通,最小化邊權之和。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 25e4+100;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
int first[maxn],des[maxn*2],nxt[maxn*2],tot;
int n,m;
LL dp[maxn],leng[maxn*2], len[maxn];
int vis[maxn],dep[maxn],fa[maxn];
int sz[maxn],wson[maxn],ttop[maxn],tfa[maxn];int k,h[maxn];
int stk[maxn],top;int l[maxn],r[maxn],dfs_clock;
inline void addEdge(int x,int y,int w){
    tot++;
    des[tot] = y;leng[tot] = w;
    nxt[tot] = first[x];first[x] = tot;
}
void dfs(int u,int fath){
    l[u] = ++dfs_clock;sz[u]=1;
    for (int t = first[u];t;t=nxt[t]){
        int v = des[t];
        if (v==fath)continue;
        LL w = leng[t];
        dep[v] = dep[u] + 1;tfa[v]=