完了完了sb選手tarjan寫掛。
考慮先tarjan縮個點雙建個圓方樹。
然後發現,確定起點和終點後,中間點的可選方案數就是 這條路徑上的所有點雙 size 之和-2 。
定義原點表示原圖中的點,方點表示圓方樹中新加入的點。
這個東西可以轉化為路徑上的方點度數之和減去原點個數。
定義點 x 的權值 d[x] ,當 x 為圓點時 d[x] = -1,否則 d[x] 等於 x 的度數。
設起點終點都是圓點的經過點 x 的路徑條數為 c[x],那麼點 x 對答案的貢獻就是 d[x] * c[x] 。
時間複雜度 $o(n)$。
#include #define clr(x) memset(x,0,sizeof (x))#define for(i,a,b) for (int i=a;i<=b;i++)
#define fod(i,b,a) for (int i=b;i>=a;i--)
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define fi first
#define se second
#define real __zzd001
#define _seed_ ('c'+'l'+'y'+'a'+'k'+'i'+'o'+'i')
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
#define outarr(a,l,r) printf(#a"[%d...%d] = ",l,r);\
for(_v2,l,r)printf("%d ",a[_v2]);puts("");
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef vector vi;
ll read()
const int n=100005*2;
int n,m,k;
vector e[n],t[n];
ll ans=0;
int dfn[n],low[n],st[n],time=0,top=0;
void add_edge(int x,int y)
int size;
void tarjan(int x) while (st[top--]!=y);
}} else
low[x]=min(low[x],dfn[y]);
}int size[n],d[n];
void dfs(int x,int pre)
ans+=2ll*v*size[x]*(size-size[x]);
}int main()
for(i,1,n)
if (!dfn[i])
size=0,tarjan(i),dfs(i,0);
cout
}
APIO2018 鐵人兩項
題意 在乙個無向圖裡面選三個點 s c f 需要能夠從 s 出發,經過 c 到達 f 點,中間不能提前經過 f 且需要是乙個簡單路徑 solution 簡單路徑當然就是園方樹了,想想怎麼統計答案 yy一下可以發現,有一條路徑 s f 中間能選的點就是路徑上的圓點和 因為在乙個點雙連通分量裡面,一定有...
APIO2018 Duathlon 鐵人兩項
給出一張無向圖,問有多少組點a,b,c滿足存在至少一條從a經過b到c的簡單路徑。首先考慮列舉點a,c,這樣每一組a,c對答案的貢獻就是可能在ac路徑之間出現的點的個數,然後我們可以對圖建圓方樹,使圓點的點權為 1 去重 方點的點權為點雙的大小,這樣ac路徑之間出現的點的個數就是a,c兩點在圓方樹上的...
APIO2018 Duathlon 鐵人兩項
不經過重點,考慮點雙 點雙,考慮圓方樹 兩個點s,t,中間路徑上,所有點雙里的點都可以經過,特別地,s,t作為割點的時候,不能往後走,也就是不能經過身後的方點 也就是,s,t 經過樹上路徑上的所有圓點和方點 把方點權值設為點雙大小 2,圓點權值設為1,s,t 路徑上的權值就是c的選擇方案數 不算s,...