6862 2020 11 14提高組模擬 剷雪

2022-05-31 11:18:12 字數 2826 閱讀 7916

一棵樹,有點權和邊權。

你需要對這棵樹進行操作,每次選擇一條路徑,代價為路徑兩端的點權和,將路徑上的邊權全部減一。

問最小代價。

支援路徑邊權加操作。

\(n\le 2*10^5,q\le 2*10^5\)

dyp對noip2018d1t1的魔改。

%%%dyp

考慮乙個節點的貢獻。那麼相當於這樣的問題:一堆數\(a_i\),每次選擇\(x\neq y,a_x,a_y>0\),將\(a_x,a_y\)分別減一。問最後剩下的數最少是多少。

結論:如果\(2\max a_i> \sum a_i\),則答案為\(2\max a_i -\sum a_i\)。否則為\(\sum a_i\mod 2\)。

證明考慮假設操作到最後只剩\(a_i\),假設\(a_i\ge 2\),如果在之前的操作中,存在\((a_j,a_k)\),那麼調整成\((a_j,a_i),(a_k,a_i)\)顯然更優,所以前面的操作都是\((a_x,a_i)\)的形式。在這種情況下,\(a_i\)取一開始的最大值是最優的,此時滿足\(2\max a_i -\sum a_i>0\)。如果不滿足,那麼最後操作剩下的數根據總和的奇偶性判定。

如果\(\sum a_i\mod 2=1\),不妨一開始給\(\max a_i\)減一。然後後面再進行判斷。

於是貢獻為:\(\max(2\max a_i-\sum a_i,0)*w\)。

對於一次修改,兩端的點暴力做,考慮中間的點:如果改的兩條邊中,其中一條為過半邊,此時\(2(a_i+\delta)-(\sum a_i+2\delta)>0\),貢獻不變;如果兩條都不是過半邊,那麼貢獻減\(2\delta\),和\(0\)取\(\max\)。

那麼可以發現:對於中間的點,貢獻是遞減的,除非它成為了一次修改的端點,否則它的貢獻不可能增加。於是可以找到即將變成\(0\)的貢獻,暴力修改。一次修改操作多\(2\)的勢能,所以總勢能為\(o(n+q)\)。

總結一下:先發現結論,然後分開處理端點和中間的點,中間的點可以快速改;端點增加勢能,中間的點減勢能,時間複雜度是對的。

後面要做的就是樹剖維護這個東西,按套路搞即可,不再贅述。

因為懶得寫所以把std貼在這裡。

#include#include#include#include#define maxn 200005

#define inf 1e18

#define ll long long

using namespace std;

int n,q,i,j,k,fai[maxn];

int em,e[maxn*2],nx[maxn*2],ls[maxn],ec[maxn*2];

ll w[maxn];

void read(int &x)

void insert(int x,int y,int z)

//shu pou

int tot,dfn[maxn],idfn[maxn],sz[maxn],g[maxn],top[maxn],dep[maxn],fa[maxn];

void dfs(int x,int p)

}void dfs2(int x,int p)

int getlca(int x,int y) else v[x]=inf; }}

// sum

struct treearray

ll sum(int x,ll s=0)

ll query(int l,int r)

} t1,t2;

void addchain(int x,int y,int z,int d)

int mid=(l+r)>>1;

maketree(x<<1,l,mid),maketree(x<<1^1,mid+1,r);

upd(x);

}void cover(int x,int l,int r,int d)

int mid=(l+r)>>1;

cover(x<<1,l,mid,d),cover(x<<1^1,mid+1,r,d);

upd(x);

}void add(int x,int l,int r,int l,int r,int d)

void clear(int x,int l,int r,int v)

int mid=(l+r)>>1;

if (v<=mid) clear(x<<1,l,mid,v);

else clear(x<<1^1,mid+1,r,v);

upd(x);

}void addit(int x,int l,int r,int v,ll s)

int mid=(l+r)>>1;

if (v<=mid) addit(x<<1,l,mid,v,s);

else addit(x<<1^1,mid+1,r,v,s);

upd(x);

}void check(int x,int y,int d)

ll getit(int x,int l,int r,int v)

void doit(int x,int z,int d)

x=fa[x];

} if (dfn[z]+1tmp0) s=p[x]; else s=k,tmp=tmp0;

} else s=k,tmp=tmp0;

ll sum0=gets(x)+d; ans+=(sum0&1)*w[x];

if (tmp*2-sum0-(sum0&1)>0) else p[x]=0;

}int main()

dfs(1,0),dfs2(1,0);

prepare();

printf("%lld\n",ans);

maketree(1,1,n);

while (q--)

}

提高組(計數)

題目鏈結 類題 氣泡排序 求長度為 n 的排列中滿足最長下降子串行長度不超過 2 且符合 p x y 的排列數。n le 10 7,t le 10 6 題意轉化 不存在三個點,使得左邊的點比中間大,右邊的點比中間小。我們要知道乙個 trick 從大到小 從小到大列舉數,嘗試將其插入當前排列,並使之合...

2018 07 08 2018提高組 模擬C組

fj準備教他的奶牛彈奏一首歌曲,歌曲由n 1 n 50,000 種音節組成,編號為1到n,而且一定按照從1到n的順序進行彈奏,第i種音節持續b i 1 b i 10,000 個節拍,節拍從0開始計數,因此從節拍0到節拍b 1 1彈奏的是第1種音節,從b 1到b 1 b 2 1彈奏的是第2種音節,依此...

NOIP提高組 矩陣

在麥克雷的面前出現了乙個有n m個格仔的矩陣,每個格仔用 或 表示,表示這個格仔可以放東西,則表示這個格仔不能放東西。現在他拿著一條1 2大小的木棒,好奇的他想知道對於一些子矩陣,有多少種放木棒的方案。因為棍子是1 2的,所以很容易就能發現,兩個被分割的塊,除了跨越兩個塊擺放木棍的方案數會對答案有影...