總的來說是比較失敗的一次考試,兩個半小時死剛T1,剩下不到四十分鍾寫T3,很倉促,爆了0。時間分配嚴重不均勻導致T3雖然想到了部分解法但是沒有來得及實現和調試。
大概考試過程就是先看了三道題,發現T3很簡單,然后開始想,三分鍾秒掉式子,大概7:22的時候就切掉了(事實證明它除了一個特判的確切掉了,而且才開考7分鍾我還看了表)。然后回去看第一題,發現貢獻分了兩半,矩形內部和矩形之間的,矩形內部可以$O(1)$得到,矩形之間的可以$O(n^2)$算出來,但是不是嚴格$O(n^2)$,因為矩形之間的臨接邊很少,沒有的時候就跳就可以了。結果寫了二十分鍾過不了大樣例,當時絕望了,調試的時候發現矩形越多數值越小我就很納悶,但是最終也沒調出來(其實是矩形邊界處理出了問題,出現了負數)。於是去做T2,基本也是秒出解(雖然不太完善但基本和正解差不多,除了合並方式上),然后十分鍾敲了兩顆樹開始維護,只過了小樣例,還是大的過不去,一直調到考試結束,於是T3愉快爆0。
下面是題解,但是這個東西仿佛不是這次考試最重要的收獲,我覺得應該是一些關於心態方面的啟發,這些我會在最后說。
T1:
是真的暴力分情況而且沒水平我實在懶得寫。
貢獻分兩部分,
內部貢獻=$ 2(x2-x1)(y2-y1) $
邊界貢獻=$ 2(邊界長度)+[一端坐標不一樣]+[另一端坐標不一樣] $
暴力枚舉加起來即可,到沒有邊界的時候$break$掉即可
T2:
比較強的一道題,考場上想到了時間軸建樹,想到了二分$size$查排名前$k$的數,想到了合並(假的線段樹合並,應該是啟發式合並)。
想了很久一直沒有結果,因為我的線段樹合並是以不遍歷全部節點為復雜度保障,但是合並了之后又無法斷定哪些點是具有貢獻的,這讓我很難受,最后我取了暴力的方法,暴力查哪些節點是被重復計算的,貢獻置0,但是會重復計算,比如說在某一個子樹的時候某個時間的已經被計算過是無用的,那么接下來子樹合並的過程中,他永遠不可能繼續有用,那么就可以不查了,但是我考場上沒有想到這一點,也許這樣可以挽救我死了的線段樹合並思路。
剩下的就是啟發式合並了。合並什么呢?我們不合並線段樹,合並操作,或許你覺得這很暴力,但是他有一定的復雜度保證$(O(nlog_2n))$。
啟發式合並其實很簡單,就是把小的塞到大的里面,這樣的話可以使得代價降到最低,復雜度怎么來的呢?假設我們每次合並的復雜度是$(O(n))$,而最劣的情況就是我每次拿一個和你一樣大的塞你,這樣我每次增大一倍,也就是會增加$(log_2n)$次,那么總的復雜度就是$O(nlog_2n)$
復雜度是有保障的,那么怎么合並呢?考慮樹上的“大”是什么意思。頹了一篇很不詳細的題解就明白了。對,是重兒子。重兒子是你最大的子樹,其他的子樹在處理完成之后直接在線段樹上打標記刪除掉即可,而重兒子萬萬不能刪除,因為你的操作要建立在重兒子的基礎上操作,這樣能夠保證復雜度。
我當時沒有想到維護線段樹的方式這么暴力。。。
把自己的操作插入重兒子的$vector$(一般情況下重兒子操作比我身上的要多,畢竟我就一個節點)然后交換位置,再把其他輕兒子的操作插入我的$vector$,這樣完成了多次啟發式合並,復雜度保證是$O(nlog_2n)$。
剛才突然想了一下,輕重划分不應當以字節點的大小作為划分依據,而應當用子節點$vector$大小,他快了$100ms$。
而每次操作都對應線段樹的一次修改或者刪除。
總復雜度就是$O(nlog_2^2n)$
每次遞歸的進行這個過程,就可以完美解決這道題。
說的比較潦草,不懂評論區。

#include<cstdio> #include<cmath> #include<vector> #include<iostream> #include<map> using namespace std; const int maxn=1e5+5,INF=0x3f3f3f3f; map<int,int> mp; vector<int> bl[maxn],dr[maxn]; int n,m,q,cnt,tot,x,y,a[maxn],first[maxn],ans[maxn],sz[maxn],son[maxn],t[maxn]; struct road{ int u,t,nxt; }eage[maxn<<1]; void add(int x,int y) { eage[++tot].u=x; eage[tot].t=y; eage[tot].nxt=first[x]; first[x]=tot; } struct SegmentTree{ int tot,data[maxn<<2],sz[maxn<<2],f[maxn<<2]; void FoundData(int x) { data[x]=data[x<<1]+data[x<<1|1]; sz[x]=sz[x<<1]+sz[x<<1|1]; } void LazyDown(int x) { if(!f[x]) return ; f[x<<1]=f[x<<1|1]=1; data[x<<1]=data[x<<1|1]=sz[x<<1]=sz[x<<1|1]=0; f[x]=0; } void Updata(int x,int l,int r,int p,int d,int num) { data[x]+=d;sz[x]+=num; if(l==r) return ; int mid=(l+r)>>1; LazyDown(x); if(p<=mid) Updata(x<<1,l,mid,p,d,num); else Updata(x<<1|1,mid+1,r,p,d,num); FoundData(x); } void Sectiondin(int x) { data[x]=sz[x]=0;f[x]=1; } int SectionQuery(int x,int l,int r,int sum) { if(sum<=0) return 0; if(l==r) return data[x]; int mid=(l+r)>>1,ans=0; LazyDown(x); if(sz[x<<1]<=sum) { ans+=data[x<<1]; ans+=SectionQuery(x<<1|1,mid+1,r,sum-sz[x<<1]); return ans; } else return SectionQuery(x<<1,l,mid,sum); } void init(int x) { for(int i=0;i<bl[x].size();i++) { int col=bl[x][i],r=dr[x][i]; if(!t[col]) Updata(1,1,m,r,1,0),t[col]=r; else if(t[col]>r) { Updata(1,1,m,t[col],-1,0); Updata(1,1,m,r,1,0); t[col]=r; } Updata(1,1,m,r,0,1); } } void dinit(int x) { Sectiondin(1); for(int i=0;i<bl[x].size();i++) t[bl[x][i]]=0; } }zt; void insert(int x,int y) { for(int i=0;i<bl[y].size();i++) { bl[x].push_back(bl[y][i]); dr[x].push_back(dr[y][i]); } bl[y].clear();dr[y].clear(); } void dfs1(int x,int fa) { sz[x]=bl[x].size(); for(int i=first[x];i;i=eage[i].nxt) if(eage[i].t!=fa) { dfs1(eage[i].t,x); sz[x]+=sz[eage[i].t]; if(sz[son[x]]<sz[eage[i].t]) son[x]=eage[i].t; } } void dfs(int x,int fa) { for(int i=first[x];i;i=eage[i].nxt) if(eage[i].t!=fa&&eage[i].t!=son[x]) { dfs(eage[i].t,x); zt.dinit(eage[i].t); } if(son[x]) dfs(son[x],x); zt.init(x); for(int i=first[x];i;i=eage[i].nxt) if(eage[i].t!=fa&&eage[i].t!=son[x]) zt.init(eage[i].t); ans[x]=zt.SectionQuery(1,1,m,a[x]); if(son[x]) { insert(son[x],x); swap(bl[son[x]],bl[x]); swap(dr[son[x]],dr[x]); for(int i=first[x];i;i=eage[i].nxt) if(eage[i].t!=fa) insert(x,eage[i].t); } } int main() { scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y);add(y,x); } for(int i=1;i<=n;i++) scanf("%d",&a[i]); scanf("%d",&m); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); if(mp[y]==0) mp[y]=++cnt,y=cnt; else y=mp[y]; bl[x].push_back(y); dr[x].push_back(i); } sz[0]=-1; dfs1(1,0); dfs(1,0); scanf("%d",&q); while(q--) { scanf("%d",&x); printf("%d\n",ans[x]); } return 0; }
T3:
說實話真的很水,三分鍾秒掉式子。大概說一下。
設$dp[i][j]$為前$i$題最高難度為$j$的概率,$w[i]$為每種難度的勞累值。
初始化:
$dp[0][0]=1$
轉移:
$dp[i][j]=inv_m(\sum \limits_{k=0}^j dp[i-1][k]+(j-1)dp[i-1][j])$
解釋一下,我當前的最大難度是$j$的可能性有兩個:
1.我之前某些點或者達到或者沒達到,但是在我這里都達到了,也就有了前面那個$\sum$
2.我之前某些點達到了而我沒有達到,那就是我有$(j-1)$種情況,所以乘上$(j-1)$。
然后就切掉了。
下面說些心態的問題:
我當時秒切T3覺得自己可厲害了可厲害了我心想這次穩了啊,爺要AK了啊,結果結果還是T1一調不出來心態就崩了,心想我怎么就調不出來。
心態這個東西很微妙,你永遠不知道他怎么調整或者有什么影響。我之前能做到的是考前不對這次考試報任何希望,可是在答題過程中卻犯了這些禁忌,期望過高了,導致一但有什么波折就死心態。如果你是真的強三道題都全秒掉的話其實也沒啥問題,但是我這種一般人做不到全秒,而且喜歡自負的覺得自己能做到全秒。雖然真的基本都正解了,可是還是能力不足沒有拿到分數。
那再立個$flag$下次要做到的是不在考前有任何期望,不再考試的時候有任何期望,心如止水。
還有另外一個東西叫舍得。
有舍才有得,考場上很多東西都要懂得取舍,不是文化課那兩下子,直接從頭做就行了的,可能丟失掉這一部分會換來更大的成績,這時候應當不再優柔寡斷,不要在僥幸的以為在過五分鍾你就能調出來,事實上到考試結束你也調不出來。舍得出去才能有得到,這是毋庸置疑的,考場上就那么多時間,拿到做多得分是最重要的,而不是去作出一道你耗費了大量心血或者一眼正解的題,沒有意義,這是考試不是刷題,刷題刷一天也沒人管你,但考試就三個半鍾頭,考的出來是他,考不出來也是他了。
但是有時候並非有舍一定會有得,可能舍棄的是一個重要的作出某道題機會。但是考場時間非常緊迫,不管對錯的選擇,都沒有時間回頭想。
為自己的選擇決定,決定了就去做,為決定負責,即使即將要為他后悔,在所不辭。
什么都無法舍棄的人什么也無法改變。
想起來之前看到的一句話,雖然沒什么關系,但至少教會你負責任。
你是個小人物,一生難得做一件大事,這個機會很寶貴,要好好珍惜。你可以偶爾發個瘋,死一個人不要緊,自己死了也不要緊,可是有些事不能逃避。樹要開花,人要長大。
——江洋
以上。