該問題可以分為兩個子問題:
1.求一種拓撲順序使點的編號最大值的更新次數最多。
2.求一種拓撲順序使點的編號最大值的更新次數最少。
對於第乙個子問題,我們貪心的想,每次走到所有能走的點中編號最小的點。這種貪心顯然是正確的,因為如果我們先走編號較大的點,再走編號較小的點,顯然不如先走編號較小的點更優一些。所以我們將原先拓撲排序中的普通佇列換成維護最小值的優先佇列即可。
priority_queue, greater>q;
for (int i = 1; i <= n; ++i)
if (!in1[i])q.push(i);
while (!q.empty())
for (int i = head[u]; i; i = e[i].nt)
if (!(--in1[e[i].to]))q.push(e[i].to);
}printf("%d\n", ans);
對於第二個子問題,如果我們將上面的維護最小值的優先佇列換成維護最大值的優先佇列就錯了,可以被下面的圖卡掉
為什麼呢?根據錯誤的貪心策略,我們會依次更新上面的那些點,但是我們如果依次更新1、2、8,再更新別的點,代價僅為3.因為錯誤的貪心每次只選擇較大的點,忽略了較小的點所能帶來的貢獻。所以我們可以改變一下貪心策略,在每次取出編號最大點後,將優先佇列中編號比它小的依次取出刪去出邊,倘若先加入的點,編號不會更新最大值,我們也將它刪去出邊,否則我們另用乙個優先佇列來儲存這些邊。這可以用類似滾動陣列的思想來解決。
priority_queueq[2];
for (int i = 1; i <= n; ++i)
if (!in2[i])q[0].push(i);
while ((!q[0].empty()) || (!q[1].empty()))
while (!q[now].empty()) }
now ^= 1;
}
以上兩個綜合利用起來就能滿分啦~
最後獻上完整**.
#include#include#includeusing namespace std;
/* it is day of judgement! */
int n, m, tot, x, y, ans, maxx, u, now;
const int n = 500010;
int head[n], in1[n], in2[n];
struct bian e[n << 1];
priority_queue, greater>q;
priority_queueq[2];
void add(int f, int t)
int main()
for (int i = 1; i <= n; ++i)
if (!in1[i])q.push(i);
while (!q.empty())
for (int i = head[u]; i; i = e[i].nt)
if (!(--in1[e[i].to]))q.push(e[i].to);
} printf("%d\n", ans);
/*上面為問題1,下面是問題2*/
ans = 0; maxx = 0;
for (int i = 1; i <= n; ++i)
if (!in2[i])q[0].push(i);
while ((!q[0].empty()) || (!q[1].empty()))
while (!q[now].empty())
}now ^= 1;
} return printf("%d", ans) == 2333;
}
洛谷10月月賽II
這道題考了矩陣旋轉 其實很考驗推公式的能力和 能力 這裡有個小技巧 可以設 x,y 為原點,然後去推公式,然後實際操作中橫座標加上x,縱座標加上y就好了。順時針 i,j j,i 逆時針 i,j j,i include define rep i,a,b for register int i a i b...
洛谷 9月月賽
題目描述 眾所周知,在一些特殊的部門,如果密碼能夠讓乙個人就解開,就會非常不安全。pic pre invoked code,預生成密碼 誕生了。這個密碼比較安全,是因為它必須由三個人保管。系統首先預先生成三個大整數a b c,計算出它們的與and 或or 和sum並儲存,然後將a b c分別告訴這三...
洛谷5月月賽
n堆石子,每次可以從第i堆中取走乙個當且僅當ni ni 1 第0堆視作0個 拿不了就輸了。問先手贏還是後手贏。奇偶性問題。不管如何分布,必定全部被拿完。include inline char gc return s inline int read while c 0 c 9 return x f i...