ZJOI2019 線段樹 解題報告

2022-04-29 20:33:14 字數 1765 閱讀 7091

聽說有人噴這個題簡單,然後我就跑去做,然後自閉感++,rp++(霧)

理性分析一波,可以發現最後形成的\(2^k\)個線段樹,對應的操作的乙個子集,按時間順序作用到這顆線段樹上。

首先考慮研究一下tag的性質,比如兩個操作時間先後是否沒有影響,操作是否可以以某種形式進行合併,然後啥也沒發現。

然後考慮一下一顆樹是否可以被壓成某個狀態,比如實際上只有\(\log\)個狀態然後去dp,發現也不行

再次冷靜分析一波,發現好像每個節點可以獨立考慮,結合上面\(2^n\),不妨考慮轉換成概率,算出每個點被打上tag的概率,在數值上同時也是它的期望,這樣我們最後乘上線段樹個數就好了。

然後這時候就只對乙個線段樹操作了,考慮咋去搞

這差不多是我做題時的心路歷程,但是我寫了好幾個做法都過不了大樣例,十分自閉,總是以為自己沒想清楚**假了之類的趕緊改過來,結果大樣例輸出了十幾個不同的答案

心態崩了.jpg

又被什麼pkuthusc的報名表填的心煩意亂的,就直接去看題解了

發現自己壓根沒有對節點分類討論的意識...

設\(dp_i\)表示\(i\)節點被打上標記的概率

考慮對區間\([4,6]\)進行操作後,節點的改變。

後文中,原來的一半因為不變,所以節點改變指變化的那一半的改變。

那考慮球一下輔助陣列\(f\)

總複雜度\(o(n\log n)\)

code:

#include #include const int size=1<<21;

char ibuf[size],*is,*it;

#define gc() (is==it?(it=(is=ibuf)+fread(ibuf,1,size,stdin),is==it?eof:*is++):*is++)

template void read(t &x)

const int mod=998244353;

const int inv2=499122177;

const int n=1e5+10;

#define mul(a,b) (1ll*(a)*(b)%mod)

inline int add(int a,int b)

#define ls id<<1

#define rs id<<1|1

int fdp[n<<2],dp[n<<2],sum[n<<2],tag[n<<2];

int n,m,in2[n];

void updata(int id)

void pushdown(int id)

void modify(int id,int l,int r,int l,int r)

dp[id]=mul(dp[id],inv2);

fdp[id]=mul(fdp[id],inv2);

pushdown(id);

int mid=l+r>>1;

if(r<=mid)

else if(l>mid)

else

modify(ls,l,mid,l,mid),modify(rs,mid+1,r,mid+1,r);

updata(id);

}int main()

else printf("%lld\n",mul(sum[1],t));

} return 0;

}

2019.5.10

ZJOI2019 語言 解題報告

3個 log 做法比較簡單,但是寫起來還是有點麻煩的。大概就是樹剖把鏈劃分為 log 段,然後任意兩段可以組成乙個矩形,就是個矩形面積並,聽說卡卡就過去了。好像這個可以被優化到兩個 log 算了,估計挺麻煩的。乙個 log 的做法看起來還挺厲害的。考慮欽定某個點算它的貢獻,於是我們要算的是所有經過它...

ZJOI2019 語言 樹鏈的並 線段樹合併

題目鏈結 考慮列舉每個點的答案,最後除以 2 即可。可以與 u 構成合法點對的點集為所有經過了 u 的鏈的並。因為這些鏈兩兩有交,所以它們的並集構成了一棵樹。考慮維護經過每個點的鏈並集的大小。一條鏈是否出現可以樹上差分,並集的具體大小就以 dfs 序為下標建線段樹,然後線段樹合併即可。複雜度 o n...

ZJOI2019 語言 樹上差分 線段樹合併

loj 3046 題意還是很好懂的,問題也很容易轉化為求每個點能到的點的個數之和,最後除以 2 即可 考慮任意一點i能到的點的個數。這些點所組成的點集等於所有包含節點 i 的鏈的點集的並集。需要哪些資訊才能維護出這個點集?由於每條鏈都包含了節點 i 因此這個點集會組成乙個連通塊 暫且這麼叫吧 這個連...