20150912 NOIP模擬 題解 總結

2021-07-05 10:00:18 字數 4440 閱讀 1321

進擊的套題系列之週末場……題目比之前做的兩套簡單,算是松了口氣,輕鬆刷200分。不過還是不能大意呀,這t3改的都快**了……

題目大意:給出 n 對數對 (x,y) ( 1≤

n≤105

,1≤x

,y≤10

9 ),保證每個數對之間的 x,y 均不相同。定義數對 a 包含數對 b 當且僅當 xa

>xb

,ya>yb

,另定義 f(

a,s)

=1表示沒有任何在數對集 s 中的數對 b 包含數對 a。現對這 n 對數對進行若干次分組操作,保證每個點都屬於乙個組。詢問每個點屬於的組編號。

分組操作如下:設當前正在進行第 cnt 次分組操作,當前仍未加入組的數對集為 s。列舉每個數對 x,如果 f(

x,s)

=1時將其加入第 cnt 組。

這題咋看有點複雜,實際上還算是比較簡單的。將數對離散化後看成點。如果將所有的點按照第一維座標從小到大排序,你會發現每次的分組操作就是「從剩餘點中找出乙個第一維座標最大的點選入組中,然後找乙個離當前點最近、且第二維座標比當前點大的點,將這個點加入組後把這個點設為當前點並繼續以上操作直至無法找到下乙個點為止」。

對於找下乙個點的操作,我們可以開乙個線段樹,其下標為每個點的第二維座標,存入的是這個點排序後的編號。如果當前點為 (x

,y)

[y+1

,n] 區間的最大值,即為下乙個點的編號。當然要將已加入組的點從線段樹中刪掉,即修改第 y 個數的值為 0。時間複雜度 o(

nlogn)

#include#include#include#define fo(i,x,y) for (int i=x;i<=y;++i)

using namespace std;

const int maxn=100000+10;

struct node a[maxn];

int n,tr[maxn*4],ans[maxn];

bool f[maxn];

int max(int x,int y)

bool cmp(node a,node b)

int main()

sort(a+1,a+n+1,cmp);

fo(i,1,n) a[i].x=a[i].y, a[i].y=i;

sort(a+1,a+n+1,cmp);

fo(i,1,n) change(1,1,n,a[i].y,i);

int now=n, cnt=0;

f[0]=1;

while (now>0)

while (now>0 && f[now]) --now;

}fo(i,1,n) printf("%d\n",ans[i]);

return 0;

}

題目大意:有個要逃跑的罪犯初始在乙個含有 n 個點、m 條無向邊的圖的 1 號點上 ( 1≤

n≤200,1≤

m≤20000

)。這個人的移動有以下規律:

首先,他絕對不會走乙個點兩次;

其次,每當他選擇走到乙個點時,他一定會走從 1 號點到這個點的最短路。保證從 1 號點走到任意點的最短路唯一。

每次他會從當前點能走到的所有相鄰點中,隨機等概率地選擇乙個點走。而如果他無點可走,就認為他成功逃跑了。

你現在要在每個點上埋伏警員抓捕他,但是他仍然有機會逃跑。一共有 s 個警員讓你調配,你現在知道每個點 i 埋伏 j 名警員能成功抓捕的概率,問在最佳調配情況下,罪犯被抓住的概率。

由於以前已經做過類似的題,且印象深刻(這是必須的,因為一天之內做6道同一型別的題感覺那是相當的爽),看到了這題幾乎是馬上想到了大致的解法。

因為這題有個神奇的規定「只走最短路」,根據這個規則可以將原圖化簡為樹。關於樹的題目最容易想到樹形dp。設 f[

i][j

] 表示該人逃跑到以

i 為根節點的子樹內被抓住的概率。先考慮不在

i放任何警員,讓罪犯逃跑到兒子節點

x 被抓住的概率,自然有:f[

i][j

]=∑k

=0sf

[i][

j−k]

+f[x

][k]

cson

[i]其中 cs

on[i

] 代表節點

i 的兒子個數。

然後再考慮在根節點放警員。注意罪犯可能在根節點被抓獲。仿照上式就有:f[

i][j

]=∑k

=0sf

[i][

j−k]

∗(1−

a[i]

[k])

+a[i

][k]

時間複雜度為均攤 o(

n)。

#include#include#include#define fo(i,x,y) for (int i=x;i<=y;++i)

using namespace std;

const int maxn=200+10, maxm=20000+10;

int n,m,s,a[maxn],b[maxm*2][3],da[maxn],db[maxn][2],dc,cnt[maxn],dis[maxn],q[maxn*100];

double p[maxn][maxn],f[maxn][maxn];

bool pd[maxn];

void spfa(int v) a[maxn*3];

int n,m,b[maxn],q[maxm][4];

char str[10];

void mark(int v,int l,int r)

int place(int v,int l,int r,int x,int del)

int ret,md=(l+r)/2;

if (x<=a[v*2].cnt) ret=place(v*2,l,md,x,del);

else ret=place(v*2+1,md+1,r,x-a[v*2].cnt,del);

a[v].cnt-=del;

return ret;

}int calc(int v)

void down(int v)

void update(int v)

void maketree(int v,int l,int r,int st)

int md=(l+r)/2;

maketree(v*2,l,md,st);

maketree(v*2+1,md+1,r,st+a[v*2].cnt);

update(v);

}void ins(int v,int l,int r,int p,int q)

down(v);

int md=(l+r)/2;

if (p<=md) ins(v*2,l,md,p,q); else ins(v*2+1,md+1,r,p,q);

update(v);

}void add(int v,int l,int r,int x,int y,int z)

down(v);

int md=(l+r)/2;

if (y<=md) add(v*2,l,md,x,y,z);

else if (x>md) add(v*2+1,md+1,r,x,y,z);

else

update(v);

}int query(int v,int l,int r,int x,int y)

int main()

if (str[0]=='q') q[i][0]=2;

}mark(1,1,n);

for (int i=m;i;--i)

if (q[i][0]) q[i][1]=place(1,1,n,q[i][1],0), q[i][2]=place(1,1,n,q[i][2],0);

else q[i][1]=place(1,1,n,q[i][1],1);

maketree(1,1,n,1);

fo(i,1,m)

if (q[i][0]==0) ins(1,1,n,q[i][1],q[i][2]);

else if (q[i][0]==1) add(1,1,n,q[i][1],q[i][2],q[i][3]);

else printf("%d\n",query(1,1,n,q[i][1],q[i][2]));

return 0;

}

這套題難度真的不怎麼大(相比之前的兩套題),但是t3的線段樹實在是太難改了-_-。改完總結原因,還是因為對線段樹的不熟練——基本原則「每次需要分割乙個區間操作時需要下傳標記」總是忘記,也因此程式出現了各種各樣的奇妙錯誤。以我現在的水平,只靠閱讀程式並不能直接發現問題,只能老實單步除錯,時間都花在這上面。看來改完題之後,也該開始鞏固一些學了很久的演算法了!

NOIP模擬題 連通

給定乙個無向圖,請編寫乙個程式實現以下兩種操作 1 d x y,從原圖中刪除連線x和y頂點的邊。2 q x y,詢問x和y頂點是否連通。這也是連通問題,考慮用並查集解決。但是這是刪除的問題耶,怎麼辦?考慮將刪除變為連通,能不能將所有操作給反過來,從最後乙個操作開始處理。當然是可以的。include ...

Noip模擬題 山峰

description在n m的棋盤上不重複的填1到n m 如果乙個數字比周圍的八個數字大,那麼他就是乙個山峰。現在告訴你所有山峰的位置,問你填數的方案數mod 12345678 input 輸入第一行兩個數字 n m意義如題目描述。接下來n 行,每行 m個字元,表示非山峰,x 表示山峰。1 n 4...

NOIP模擬題 訂餐

題目描述 這個週末,joe 一如既往的拿出了外賣的點餐單。選單上從上到下列著 n 個菜,每個菜 joe 會列出乙個美味值。joe 這次希望從選單上連續地點 k 個菜,並且美 味值之和最大。但在計算美味值之和時,joe 有自己的一套計算方法 由於他會按照順序享受這 k 個菜,並且越吃越後面的菜他就越享...