第2問其實沒有什麼特殊的地方,所以這裡只講第1問的做法,然後第2問的思路也是一樣的。
對於這種環套樹的問題,可以考慮先刪去環上的一條邊,這樣它就變成了一棵樹,然後統計樹上的答案,最後加上必須通過刪去的邊的答案。
樹上的做法
這類問題很容易聯想到點剖。
對於當前乙個根為x的子樹,用s[i]表示深度為i的節點的個數,初始為0。然後列舉x的每乙個子樹,然後bfs一下,統計答案,遍歷完這個子樹後再加到s陣列裡。
環上的做法
設環上的點為p1,p2……pn,其中(pn,p1)是上面刪去的那條邊。對於兩個點(u,v),其中u是pi的子節點,v是pj的子節點。設dep[i]為節點i到環上任意一點的最短距離。如圖所示:
那麼它能被統計入答案,當且僅當滿足所有下列條件:
1. dep[u]+dep[v]+i+n-j≤k //經過刪去的邊
2. dep[u]+dep[v]+(j-i)>k //不經過刪去的邊
3. i< j
但是可以發現,當i≥j時,j-i≤0,然後無論何時此時都滿足i+n-j>0。此時如果滿足第2個條件,那麼第乙個條件就一定不滿足。所以可以無視掉第3個條件。
然後設x[u]=dep[u]+i(u為i的子節點),y[u]=dep[u]-i,那麼條件變成:
1. x[u]≤k-n-y[v]
2. y[u]>k-x[v]
統計答案就可以用主席樹來完成了。
注意細節
#include
#include
#include
using namespace std;
const
int maxn=100005,maxm=200005;
typedef
long
long ll;
int n,m,tot,id,root,size,v,n,s[maxn],h[maxn],e[maxm],next[maxm],v[maxn],data[maxn],size[maxn],dep[maxn],e[maxn],id[maxm];
int fa[maxn],d[maxn],root[maxm],l[maxm*20],r[maxm*20],s1[maxm*20];
ll ans1,ans2,t[maxn],s2[maxm*20];
bool visit[maxn],bz[maxn];
struct op
a[maxm];
bool cmp(op a,op b)
void add(int x,int y,int
id)void find(int x)
}void calc(int x,int y)
m=max(m,size-size[x]);
if (mvoid get_dep(int x,int y)
}void dfs(int x)
ans1+=cnt;
ans2+=sum*v[data[j]];
}else
break;
for (k=h[data[j]];k;k=next[k]) if (id[k]!=id && !visit[e[k]] && dep[e[k]]==dep[data[j]]+1)
data[++tot]=e[k];
}for (j=1;j<=tot;j++)
}ans1+=cnt; ans2+=ss*v[r];
data[tot=1]=r;
for (j=1;j<=tot;j++)
for (j=2;j<=tot;j++)
for (i=h[r];i;i=next[i]) if (id[i]!=id && !visit[e[i]])
}bool find(int x,int s,int y)
for (int i=h[x];i;i=next[i]) if (id[i]!=id && !visit[e[i]] && find(e[i],s+1,y)) return
1; return0;}
void insert(int l,int r,int g,int v,int y,int &x)
s1[x]++; s2[x]+=v;
if (l==r) return;
int mid=(l+r)/2;
if (g<=mid) insert(l,mid,g,v,l[y],l[x]);
else insert(mid+1,r,g,v,r[y],r[x]);
}int get1(int l,int r,int g,int x)
ll get2(int l,int r,int g,int x)
int get(int x)
int main()
find(1);
for (id=1;bz[id];id++);
for (int i=1;i<=n;i++) scanf("%d",&v[i]);
memset(visit,0,sizeof(visit));
size=n;
dfs(1);
memset(visit,0,sizeof(visit));
find(id,1,e[id]);
memset(bz,0,sizeof(bz));
for (int i=1;i<=v;i++) bz[d[i]]=1;
n=0;
for (int i=1;i<=v;i++)}}
}sort(a+1,a+n+1,cmp);
a[0].x=root[0]=s1[0]=s2[0]=tot=0;
memset(l,0,sizeof(l)); memset(r,0,sizeof(r));
for (int i=1;i<=n;i++) insert(0,m+v,a[i].y+v,a[i].e,root[i-1],root[i]);
for (int i=1;i<=n;i++)
}printf("%lld %lld\n",ans1,ans2);
fclose(stdin); fclose(stdout);
return
0;}
GDOI 2016 Day1 T2 最長公共子串
給出兩個字串a和b,求最長公共子串。其中b串中有k個區間的字元可以任意調換。a b 2000,k 100000 乙個顯然的性質就是,如果兩個區間有交集,那是可以並成乙個的 然後我們就來考慮怎麼做。乙個十分樸素的想法,是列舉兩個的起點,然後暴力往後匹配 這樣做是o n3 o n 3 的,不滿足要求 但...
藍橋python 湊算式 2016 第三題
題目描述 這個算式中 a i代表 1 9 的數字,不同的字母代表不同的數字。比如 6 8 3 952 714 就是一種解法,5 3 1 972 486 是另一種解法。這個算式一共有多少種解法?這個題目還是算很簡單的了 相當於就是1 9的全排列後對應以上的a i 湊算式 import itertool...
DAY2 模擬考試第三題 情書的代價
3 情書的代價 cost.pas c c 題目描述 在如今的應試教育下,以學業為重 成為了分手的主要理由。佳佳想知道寫情書到底會花去多少時間以決定是否值得犧牲這些時間來討好girlfriend。他得到了乙份evalls的所有情書的副本,他希望從中得知evalls寫這些情書最少花了多少時間。完成每封情...