洛谷ios
忽然特別想罵人,原本我考場現切了的,結果WA了幾個點,剛剛拿代碼一看有個地方忘記取模了。ide
首先發現終止態必定是全部點都向\(n\)連邊(看樣例圖解就知道了)
那麼大力猜測一下第一問的答案必定是\(n-3-\)和\(n\)號點直接相連的邊數。
手玩一下,發現這樣一件事情:和\(n\)直接相連的全部邊把多邊形分割成了若干個區間,每一個區間都用\([l,r]\)表示。
對於\([l,r]\)這個區間,由於已經分割出來了,也就是除了\(l-n,r-n\)以外,沒有直接和\(n\)相連的邊,那麼發現這裏執行一次旋轉操做一定會選擇到\((l,r)\),那麼只須要找到\(b\)點,顯然\(b\)也是惟一肯定的,那麼直接在\(l\)的出邊中找到小於\(r\)的最大值就好了,這個點就是\(b\)。
發現此次操做執行完以後,這個區間被劃分紅了兩個部分,只須要遞歸處理就好了。
因而,除了一開始就和\(n\)號點直接相連的邊以外,每次劃分必定把區間分割成兩個部分,而且分割操做惟一,所以咱們能夠把這個過程用一個二叉樹來表示。
考慮計算方案數,一個節點表示這個點所表明的操做必須在左右兩個兒子以前進行,而分割完此次以後,左右兩個兒子之間就獨立了,所以等價於左側有一個操做序列,右側有一個操做序列,須要把他們兩合併,這裏貢獻的方案數就是一個組合數。因此方案數就是每一個節點合併兩個兒子的方案數的乘積。最後再把全部被和\(n\)相連的邊劃分出來的區間再乘一下拼接的組合數就是答案。
那麼咱們能夠處理單次詢問了。
繼續考慮提早執行一次旋轉操做對於答案的影響,相似\(Splay\),發現此次操做就是把一個點\(rotate\)一下(感性理解或者手玩一下就知道爲何了)。
那麼這裏須要分類討論,若是這個點存在父親,直接\(rotate\)就好了,除掉本來的貢獻再乘上新的貢獻就能夠了。
不然這個點不存在父親,即這個點是劃分的第一次操做,提早旋轉以後就變成了和\(n\)相連的邊,這裏會把第一問的答案減一,而後把本來的方案數除掉,再乘上直接把左右兩個兒子當成被和\(n\)相連的邊分割的方案數就好了。
時間複雜度一個\(log\)。(由於我要用\(map\)儲存每一個點對應的是哪條邊,以及在建樹的時候須要\(lower\_bound\))
下面是考場代碼(把那個鬼取模給補上了spa
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<map> using namespace std; #define ll long long #define MAX 200200 #define MOD 1000000007 inline int read() { int x=0;char ch=getchar();bool fl=false; while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')fl=true,ch=getchar(); while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar(); return fl?-x:x; } int W,n,ans,Ans=1; int jc[MAX],inv[MAX],jv[MAX]; int C(int n,int m){if(n<0||m<0||n<m)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;} int InvC(int n,int m){return 1ll*jv[n]*jc[m]%MOD*jc[n-m]%MOD;} int Merge(int n,int m){return C(n+m,n);} int InvMerge(int n,int m){return InvC(n+m,n);} int ch[MAX][2],tot,sz[MAX],fa[MAX]; int rt[MAX]; vector<int> E[MAX]; map<pair<int,int>,int> M; void Divide(int &x,int ff,int l,int r) { if(r-l<=1)return;x=++tot;sz[x]=1;fa[x]=ff; int p=lower_bound(E[r].begin(),E[r].end(),l+1)-E[r].begin(); p=E[r][p];M[make_pair(l,r)]=x; Divide(ch[x][0],x,l,p);Divide(ch[x][1],x,p,r); sz[x]+=sz[ch[x][0]]+sz[ch[x][1]]; Ans=1ll*Ans*Merge(sz[ch[x][0]],sz[ch[x][1]])%MOD; } int main() { freopen("polygon.in","r",stdin); freopen("polygon.out","w",stdout); W=read();n=read(); jc[0]=jv[0]=inv[0]=inv[1]=1; for(int i=2;i<=n+n;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD; for(int i=1;i<=n+n;++i)jc[i]=1ll*jc[i-1]*i%MOD; for(int i=1;i<=n+n;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD; for(int i=1;i<=n-3;++i) { int u=read(),v=read(); E[u].push_back(v); E[v].push_back(u); } for(int i=2;i<n;++i)E[i].push_back(i-1),E[i].push_back(i+1); E[1].push_back(2);E[1].push_back(n); E[n].push_back(1);E[n].push_back(n-1); for(int i=1;i<=n;++i)sort(E[i].begin(),E[i].end()); for(int i=0,l=E[n].size();i<l-1;++i)Divide(rt[i],0,E[n][i],E[n][i+1]); int SS=0; for(int i=0,l=E[n].size();i<l-1;++i)Ans=1ll*Ans*Merge(SS,sz[rt[i]])%MOD,SS+=sz[rt[i]]; int cnt=n-1-E[n].size(); if(!W)printf("%d\n",cnt); else printf("%d %d\n",cnt,Ans); int Q=read(); while(Q--) { int a=read(),b=read();if(b<a)swap(a,b); int p=M[make_pair(a,b)]; int pcnt=cnt-(fa[p]?0:1); if(!W){printf("%d\n",pcnt);continue;} else printf("%d ",pcnt); int pans=Ans; if(fa[p]) { int f=fa[p],k=ch[f][1]==p; pans=1ll*pans*InvMerge(sz[ch[p][0]],sz[ch[p][1]])%MOD; pans=1ll*pans*InvMerge(sz[ch[f][0]],sz[ch[f][1]])%MOD; pans=1ll*pans*Merge(sz[ch[f][k^1]],sz[ch[p][k^1]])%MOD; pans=1ll*pans*Merge(sz[f]-sz[p]+sz[ch[p][k^1]],sz[ch[p][k]])%MOD; } else { pans=1ll*pans*InvMerge(sz[ch[p][0]],sz[ch[p][1]])%MOD; pans=1ll*pans*InvMerge(SS-sz[p],sz[p])%MOD; pans=1ll*pans*Merge(SS-sz[p],sz[ch[p][0]])%MOD; pans=1ll*pans*Merge(SS-sz[p]+sz[ch[p][0]],sz[ch[p][1]])%MOD; } printf("%d\n",pans); } return 0; }