1. 程式人生 > >#LOJ2541. 「PKUWC2018」獵人殺(分治+NTT)

#LOJ2541. 「PKUWC2018」獵人殺(分治+NTT)

題意

n n 個人,每個人有一個權值 w i w_i

,每次隨機殺一個人,殺第 i i 個人的概率是 w i
j [ j   i s   a
l i v e ] \frac{w_i}{\sum_j[j\ is \ alive]}
,求第一個人最後一個死的概率,對 998244353 998244353 取模。

題解

這題不是很難但是我自己太sb了所以看了很久。

第一個人最後一個死就代表恰好有 0 0 個人在第一個人之後死,算這個是個很套路的東西用容斥轉化為至少,那麼設 f ( S ) f(S) 為至少有 S S 集合的人在第一個人之後死的概率,那麼 a n s = ( 1 ) S f ( S ) ans=\sum(-1)^{|S|}f(S) ,現在我們轉化成了求 f ( S ) f(S) ,實際上這個東西我們可以寫出一個式子:
f ( S ) = w 1 w 1 + w [ w S ] f(S)=\frac{w_1}{w_1+\sum w[w \in S]} 這個東西理解起來也不是很難,每次只有選擇 S S 集合內的人擊殺或者 1 1 擊殺的時候才會影響他們的相對死亡順序,而選中這裡面的人擊殺時必須要擊殺第一個人才滿足 S S 集合都在 1 1 之後死,那麼我們發現 f ( S ) f(S) 只與 w [ w S ] \sum_w[w\in S] 有關,又因為題目中的條件有 w 1 0 5 \sum w\le10^5 ,我們可以考慮計算出每個值的容斥係數,可以直接設 f [ i ] [ j ] f[i][j] 表示考慮前 i i 個人,權值和為 j j 時的容斥係數,用揹包的方法轉移就是 f [ i ] [ j ] = f [ i 1 ] [ j ] f [ i 1 ] [ j w i ] f[i][j] = f[i - 1][j] - f[i - 1][j - w_i] ,這樣子就可以得到 50 50 分的好成績了,但實際上由於資料比較水,在LOJ上可以通過 80 80 分。

#include <bits/stdc++.h>

#define x first
#define y second
#define pb push_back
#define mp make_pair
#define inf (0x3f3f3f3f)

using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

template<class T>inline T read(T &_) {
	T __ = getchar(), ___ = 1; _ = 0;
	for (; !isdigit(__); __ = getchar()) if (__ == '-') ___ = -1;
	for (; isdigit(__); __ = getchar()) _ = (_ << 3) + (_ << 1) + (__ ^ 48);
	return _ *= ___;
}

template<class T>inline bool chkmax(T &_, T __) { return _ < __ ? _ = __, 1 : 0; }
template<class T>inline bool chkmin(T &_, T __) { return _ > __ ? _ = __, 1 : 0; }

inline void proStatus() {
	ifstream t("/proc/self/status");
	cerr << string(istreambuf_iterator<char>(t), istreambuf_iterator<char>());
}

const int N = 1 << 18 | 1; 
const int mod = 998244353;

int n, sum, ans, w[N], f[N];

inline int add(int x, int y) { return (x += y) < mod ? x : x - mod; }

inline int qpow(int _, int __) {
	int ___ = 1; 
	for (; __; __ >>= 1, _ = 1ll * _ * _ % mod) 
		if (__ & 1) ___ = 1ll * ___ * _ % mod;
	return ___;
}

int main() {
#ifdef ylsakioi
	freopen("2541.in", "r", stdin);
	freopen("2541.out", "w", stdout);
#endif

	read(n), f[0] = 1;
	for (int i = 1; i <= n; ++ i) 
		sum = add(sum, read(w[i]));
	sum = add(sum, mod - w[1]);
	for (int i = 2; i <= n; ++ i) 
		for (int j = sum; j >= w[i]; -- j) 
			f[j] = add(f[j], (mod - f[j - w[i]]));
	for (int i = 0; i <= sum; ++ i) 
		ans = add(ans, 1ll * f[i] * qpow(i + w[1], mod - 2) % mod);
	printf("%lld\n", 1ll * ans * w[1] % mod);

	return 0;
}

我們設出這個東西的生成函式,第 i i a i x i a_ix^i 代表當前 d p i = a i dp_i=a_i ,那麼每次轉移就是乘上 ( 1 x w i ) (1-x^{w_i}) ,分別代表是否把當前這個人加入集合的決策,那麼這個生成函式就是:
i = 2 n ( 1 x w i ) \prod_{i = 2}^n(1-x^{w_i})
這個東西直接分治,合併兩個區間的資訊的時候用NTT計算就好了,這個東西開陣列有點麻煩,那麼我們可以類似於線段樹動態開點回收空間的方法一樣,用完以後利用以前的空間,分治出最深的一條鏈深度應該是 log n \log n 的,那麼我們開 log n \log n 個數組就好了,複雜度 O ( n log 2 n ) O(n\log ^2n)

Codes

#include <bits/stdc++.h>

#define x first
#define y second
#define pb push_back
#define mp make_pair
#define inf (0x3f3f3f3f)

using namespace std;

typedef long long ll;
typedef pair<int, int> PII;

template<class T>inline T read(T &_) {
	T __ = getchar(), ___ = 1; _ = 0;
	for (; !isdigit(__); __ = getchar()) if (__ == '-') ___ = 
            
           

相關推薦

#LOJ2541. PKUWC2018獵人分治+NTT

題意 有 n n n個人,每個人有一個權值

LOJ2541:PKUWC2018獵人

max pow bit del esp 一個 wap 選中 display 傳送門 Sol 第一步就不會 問題轉化 殺人後將其打上標記,仍可以以他為目標重復選,直到選到一個未打標記的人。 這和原問題等價,而且這樣每輪選中每人的概率都不變,只是遊戲變成了無窮輪數 這樣就好做

LOJ #2541PKUWC2018獵人

這樣$ PKUWC$就只差一道鬥地主了 假裝補題補完了吧..... 這題還是挺巧妙的啊...... LOJ # 2541 題意:每個人有一個嘲諷值$a_i$,每次殺死一個人,殺死某人的概率為$ \frac{a_i}{a_{alive}}$,求第一個人最後死的概率 資料範圍:$ 1 \leq a_i

PKUWC2018獵人

題面 題解 考慮開槍時,如果打到死掉的獵人就再來一槍 而不是不能打死掉的獵人 假設$A$集合中$(1\notin A)$所有都在$1$之後死 設$\sum_{i=1}^nw[i]=S,\sum_{i\in a}w[i]=T$,則概率為$\frac {w_1}{w_1+T}$ 前面的容斥係數可以用生

LibreOJ #2541.PKUWC 2018獵人 分治NTT+容斥原理

題意 獵人殺是一款風靡一時的遊戲“狼人殺”的民間版本,他的規則是這樣的: 一開始有nn個獵人,第ii個獵人有仇恨度wiwi,每個獵人只有一個固定的技能:死亡後必須開一槍,且被射中的人也會死亡。 然而向誰開槍也是有講究的,假設當前還活著的獵人有[i1...i

長樂集訓 2017 Day10劃分序列 二分 dp

color -html math != mes 序列 spa cto html 「長樂集訓 2017 Day10」劃分序列 題目描述 給定一個長度為 n nn 的序列 Ai A_iA?i??,現在要求把這個序列分成恰好 K KK 段,

loj2537 PKUWC2018Minimax 【概率 + 線段樹合並】

tdi size pri 題目 概率 log mini source ima 題目鏈接 loj2537 題解 觀察題目的式子似乎沒有什麽意義,我們考慮計算出每一種權值的概率 先離散化一下權值 顯然可以設一個\(dp\),設\(f[i][j]\)表示\(i\)節點權值為\(j

【LOJ】 #2540. PKUWC2018隨機算法

namespace 就是 int div esp 排列 template 每次 LV 題解 感覺極其神奇的狀壓dp \(dp[i][S]\)表示答案為i,然後不可選的點集為S 我們每次往答案裏加一個點,然後方案數是,設原來可以選的點數是y,新加入一個點後導致了除了新加的點之

LOJ #2719. NOI2018冒泡排序組合數學 + 樹狀數組

git stderr 好的 sizeof 序列 下界 deb efi 如果 題意 給你一個長為 \(n\) 的排列 \(p\) ,問你有多少個等長的排列滿足 字典序比 \(p\) 大 ; 它進行冒泡排序所需要交換的次數可以取到下界,也就是令第 \(i\) 個數為 \(a_

LuoguP1220 關路燈區間dp

-s 輸出格式 article sin 最小值 區間 都是 algorithm 單位 題目描述 某一村莊在一條路線上安裝了n盞路燈,每盞燈的功率有大有小(即同一段時間內消耗的電量有多有少)。老張就住在這條路中間某一路燈旁,他有一項工作就是每天早上天亮時一盞一盞地關掉這

日常訓練All FriendsPOJ-2989

any cin memset ace string ear key return ans 題意 分析 代碼 #include <iostream> #include <cstring> #include <algorithm> #defi

UVA10298 Power StringsKMP

length 如果能 href uva -a 一個 png cin stream 題目描述 PDF 輸入輸出格式 輸入格式: 輸出格式: 輸入輸出樣例 輸入樣例#1: 復制 abcd aaaa ababab . 輸出樣

UVA11181 Probability|Given概率

表示 之前 badge copy 組合 否則 事件 col block 題意翻譯 有n個人要去買東西,他們去買東西的概率為p[i]。 現在得知有r個人買了東西,在這種條件下,求每個人買東西的概率。 感謝@s_r_f 提供翻譯 題目描述 PDF 輸入輸出格式

51Nod1639綁鞋帶概率

group 包含 sta anti cor 兩個 radi 想象 src 1639 綁鞋帶 基準時間限制:1 秒 空間限制:131072 KB 分值: 20 難度:3級算法題 收藏 關註 有n根鞋帶混在一起,現在重復n次以下操

2018.10.27 loj#6035. 雅禮集訓 2017 Day4洗衣服貪心+堆

傳送門 顯然的貪心題啊。。。考試沒調出來10pts滾了妙的一啊 直接分別用堆貪心出洗完第 i i i件衣

日常訓練Balancing ActPOJ-1655

題意與分析 樹的重心板子題。 值得考慮的是,重心究竟有哪些優秀的性質? 這裡是一些網上能看到的性質: (判定性質)找到一個點,其所有的子樹中最大的子樹節點數最少(子樹可以“倒著看”),那麼這個點就是這棵樹的重心。 以這個點為根,那麼所有的子樹(不算整個樹自身)的大小都不超過整個樹大小的一半。

JLOI2011LuoguP4568飛行路線分層圖最短路

題目描述 Alice和Bob現在要乘飛機旅行,他們選擇了一家相對便宜的航空公司。該航空公司一共在nn個城市設有業務,設這些城市分別標記為00到n-1n−1,一共有mm種航線,每種航線連線兩個城市,並且航線有一定的價格。 Alice和Bob現在要從一個城市沿著航線到達另一個城市,途中可以進行轉機。航空公司對

NOIP2013LuoguP1967貨車運輸最大生成樹 倍增 LCA LuoguP4180 【模板】嚴格次小生成樹[BJWC2010]倍增 LCA Kruscal

題目描述 AA國有nn座城市,編號從 11到nn,城市之間有 mm 條雙向道路。每一條道路對車輛都有重量限制,簡稱限重。現在有 qq 輛貨車在運輸貨物, 司機們想知道每輛車在不超過車輛限重的情況下,最多能運多重的貨物。 輸入輸出格式 輸入格式: &nb

NOIP2013LuoguP1967貨車運輸最大生成樹 倍增 LCA

!= noi ora 輸出 tdi ott 限制 格式 否則 題目描述 AA國有nn座城市,編號從 11到nn,城市之間有 mm 條雙向道路。每一條道路對車輛都有重量限制,簡稱限重。現在有 qq 輛貨車在運輸貨物, 司機們想知道每輛車在不超過車輛限重的情況下,最多能運多重