山西胡策 7

2022-05-11 22:34:32 字數 4143 閱讀 4457

a.

題意:給乙個有向圖無環連通圖,求新增一條邊x->y後有向生成樹的方案數。(n<=100000)

#include using namespace std;

typedef long long ll;

const int n=100005, mo=1000000007;

int ihead[n], cnt, n, m, x, y;

struct e e[n<<1];

void add(int x, int y) ; ihead[x]=cnt; }

int inv[n], in[n], d[n], q[n], front, tail, f[n];

int main() ^ d(i)$,其中$d(i)$表示$i$的入度。

考慮加邊,首先考慮上面的做法,那麼答案顯然是多加了的,而多加的部分就是由乙個新環以及環外其他點組成的生成樹的方案。由於環上一定存在x->y這條邊,因此我們只需要計算原圖y->x的路徑數及其答案即可,即令

$$f(i) = \sum_ \prod_ d(j)$$

由於原圖為拓撲圖,因此按照拓撲序dp即可,即

$$f(i) = \sum_ \frac$$

最後特判一下y=1的情況= =

b.題意:給一棵n個點的樹,給出p條帶權路徑,q次詢問,每次詢問一條路徑,問這條路徑包含的所有帶權路徑中第k小的權值。(n,q,p<=40000)

#include using namespace std;

const int n=40005;

int ihead[n], cnt, n, p, q, ff[n], ll[n], ans[n], tot, now[n], k[n], w[n], c[n];

struct e e[n<<1];

struct d d[n*9];

void add(int x, int y) ; ihead[x]=cnt; e[++cnt]=(e); ihead[y]=cnt; }

void add1(int x, int y, int zf, int c, int id) ; }

void add2(int x1, int x2, int y1, int y2, int id)

void dfs(int x, int f=-1)

bool cmp(const d &a, const d &b)

//printf("mid:%d\n", mid);

//for(int i=l; i<=r; ++i) if(d[i].c) printf("id:%d : %d\n", d[i].id, now[d[i].id]);

nl=l, nr=l+ct;

for(int i=l; i<=r; ++i)

if(!d[i].c)

else // printf("%d %d\n", nl, nr); puts("");

for(int i=l; i<=r; ++i) d[i]=t[i];

if(f1) fz(l, mid, l, nl-1);

if(f2) fz(mid+1, r, nl, r);

}int main()

} }fz(1, 1e9, 1, tot);

for(int i=1; i<=q; ++i) printf("%d\n", ans[i]);

return 0;

}

好神的題= =(hnoi太神啦。。

本題需要想到:

1、用dfs序來表示路徑覆蓋。

2、二分答案

3、整體二分

首先考慮dfs序覆蓋,約定,對於乙個點$x$,$ff(x)$為dfs序,$ll(x)$為子樹訪問完後此時的dfs序

考慮一條路徑$(x, y)$,$ff(x)<=ff(y)$。

令$f=lca(x, y)$

1. $x!=f$,此時能覆蓋路徑$(x, y)$的路徑一定是從$x$的子樹中取乙個點(包括自己)和從$y$的子樹取乙個點然後形成的路徑,因此dfs序的表示就是路徑$(a, b)$能覆蓋路徑$(x, y)$的充要條件是$ff(a) \in [ff(x), ll(x)], ff(b) \in [ff(y), ll(y)]$

2. $x==f$,此時能覆蓋路徑$(x, y)$的路徑$(a, b)$一定是$ff(a) \in [1, ff(z)-1], ff(b) \in [ff(y), ll(y)]$或者$ff(a) \in [ff(y), ll(y)], ff(b) \in [ll(z)+1, n]$,$z$表示$x$包含$y$的子樹的根。容易發現前者和後者將所有情況包含了。

那麼對於乙個詢問$(x, y)$,$ff(x)<=ff(y)$(注意保持順序,因為上面的分析都是左小右大的,否則會多算),將他們當做二元座標看待。於是我們二分權值,每一次都找有多少個權值小於等於當前答案的能覆蓋這個點的矩形即可。這是經典問題= =先按x軸排序後後用bit維護y即可。

可是這裡很多組詢問= =每一次詢問都二分一次太浪費= =於是我們採用整體二分,就是一次二分統計全部的。(類似cdq分治= =或者說= =cdq分治本來就是整體二分的一種情況。。)

這個就簡單了,按照如上演算法做就行了。。總體複雜度為$o(nlogn)$($log 10^9$ 這是乙個常數【捂臉熊】。這個複雜度似乎要均攤分析啊= =大概就是最終都會落到$o(q)$個點)

然後注意一下我的sb錯。。。

1、我整體二分的時候分到右半區間的第k小沒有減去當前的得到的。。因此後面分治的時候就錯了= =(因為前半區間的插入並沒有到後半區間來= =)

2、插入矩形的時候x軸和y軸搞錯= =

c.題意:給出(i, j)之類的約束表示要j必須先i,問1盡量靠前、2盡量靠前、3盡量靠前以此類推的最優方案,或輸出無解。

#include using namespace std;

const int n=100005;

int cnt, ihead[n], in[n], ans[n], tot, n, m;

struct e e[n];

void add(int x, int y) ; ihead[x]=cnt; }

priority_queueq;

int main()

memset(ihead, 0, sizeof(int)*(n+1));

memset(in, 0, sizeof(int)*(n+1));

cnt=0;

} return 0;

}

亂搞才5分啊qaq我竟然忘記我這題做過,我是有多弱tat(就算做過我也不會證明= =)

於是果斷orz zyf神犇

這裡有兩道看起來十分相似的題目:

查錯  &&  拓撲編號

我們對比一下這兩道題目:

查錯要求:求乙個拓撲序,使得字典序盡可能小。

拓撲編號要求:求乙個拓撲序,使得1盡量往前,在此情況下,2盡量往前。一次類推。

需要注意,這兩個要求肯定是不同的。

例如在拓撲編號中 4 1 2 3 5是比 3 1 4 2 5 (當然不一定合法)優的,因為2的位置比較靠前。

我們分別來敘述這兩個問題的解法:

查錯 只要按正常的拓撲排序,只不過把queue改成priority_queue即可。正確性顯然。

拓撲編號 我們反向拓撲排序,每次取出出度為0的最大的節點,標號,然後用它去更新其他點的出度。

這個演算法的證明我是在這裡看到的:演算法證明

這裡再口胡一下:

不妨認為我們這樣得到的不是最優解,那麼令這樣得到的序列為a,然後最優解是b。

我們從後往前開始找到第一位兩個序列不同的一位設為k,那麼a[k]!=b[k],且a[k]>b[k]。(由a的構造方式可知)(先假設這個k存在,再證出矛盾)

再設a[k]出現的b的p位置,即b[p]=a[k]。再設b[p] b[p+1]……b[k]這個子串行為c。

那麼b[p]一定不是c中的最小元素,因為有b[k]然後不妨設b[q]為c的最小元素。然後我們把b[p]移到b[k]的位置,得到序列bb。

如果bb合法的話,那麼我們就得到了乙個比b優的解,這與b是最優解矛盾。

(因為b[q]的位置前移了一位,我們要求編號小的盡可能靠前)

但bb顯然是合法的。因為在a序列中k以及後面的是合法的,那麼b後面也這麼做一定也是合法的。

所以一定不存在某個k,使得a[k]!=b[k]。也就是說a=b。

所以演算法正確性得證。(證法和鏈結裡有點不一樣,但我認為也是正確的)

11 08隊內胡策

總結就是t3想錯直接翻車233.第二題給你兩個日期,問這兩個日期差了多少毫秒。兩行,每行乙個日期,日期格式保證為 yyyy mm dd hh mm ss這種形式。第二個日期時間一定比第乙個日期時間要大兩個日期的年份一定都是 21 世紀的年份。一行乙個整數代表毫秒數。2000 01 01 00 00 ...

隊內胡策 2017 10 15

輸入描述 乙個數 t 接下來 t 行,每行乙個長度為 n 的 01 串 輸出描述 乙個數 p 表示 dqs 消耗的神力 樣例輸入 2 000011 0101 樣例輸出 26 10 資料範圍及提示 1 t 50 60 1 n 1000 100 1 n 100000 純暴力,沒什麼技術含量,但是在這道題...

隊內胡策 2017 10 12

t1 t2 狀壓dp t3 原創題 函式求和 題目描述 有乙個含有 n 個數字的序列 a,元素標號 1 到 n。同時你有 n 個函式,標號為 1 到 n。第 i 個函式函式值為序列中下標為 li 到 ri 的元素和。現在你需要維護以下兩種操作 1 x y 將序列中下標為 x 的元素修改為 y2 s ...