NOIP2024 前集訓:NOIP2024加賽 5(兼假期閒話)

卡布叻_周深發表於2024-11-17

前言

music
《浮光》

看指尖撥響蝴蝶 扇動一場離別

我推開無聲歲月 續夢一頁

你我只是打個照面 可曾有過誓約

走進熟悉卻 陌生的思念

啊……

啊……

你的眼眸 裝滿了時間

你的身後 擁故事成篇

此生如夢 願細數流年

與你同寫 滄海桑田

浮光掠影 重山彩雲間

你的伏線 穿越千百年

人生不過 恍惚三萬天

漫漫人間 留戀流連

你說那月光 照過同樣城牆

永恆的剎那的 此刻沉默無話

想問你這星空 是否不曾變

是啊 是啊 我們望著它

風吹過耳旁 古遠的歌唱啊

這是我也是你 不曾遺忘的啊

是來路是去處 是你在回答

去吧 去啊 總會相遇吧

你的眼眸 裝滿了時間

你的身後 擁故事成篇

此生如夢 願細數流年

與你同寫 滄海桑田

浮光掠影 重山彩雲間

你的伏線 穿越千百年

人生不過 恍惚三萬天

漫漫人間 留戀流連

放假前

明天(發的時候已經是今天了)就要放假了開心O(∩_∩)O~~,就放一天不開心 ̄へ ̄。

還要開家長會?不知道我們這種期中沒考選科也毫無懸念的過來開家長會的意義何在……

明天還有模擬賽?到底打不打?真打的話打一半就得走。

晚上(21:10 左右)網沒了?feifei 說出故障了,過了一會兒修復了。

宿舍的遠古電話交換機太破了老是佔線煩死了。

中午讓我推歌,好耶放周深的,想要放《一期一會》,huge 說要放大家可能聽過的跟著唱,不是都沒聽過《未聞花名》嗎?改成《浮光》,還要我跟著唱,不是我耳朵有毛病不會跟著唱,深深唱得那麼好聽靜靜欣賞不好麼,要麼只聽要麼自己唱,好像機房聽過這個的也不多(就算聽過也唱不上去),一幫人默默地聽沒人唱。

huge 坦白是往年學長加的 hack,還科普了為什麼飲水機會漏。

field 告訴我們今天不用打模擬賽,也不用開家長會,好耶O(∩_∩)O~~,為了不打擾高二的把我們拉到 504,高二全區 505,504 比 505 網快多了,下課後打電話把老媽從教室喊出來,收拾完東西就要走,但是 10:20 之前不讓走,於是和 hangry、ccxswl 去圖書館看書到 10:20 走人。

放假後

以為只有 \(55\) 抽?實際上全扔進去又可以換十多抽,於是再肝一肝即可。

出草神了O(∩_∩)O~~,\(81\) 抽才出生氣了 ̄へ ̄。

沒什麼武器能用,只好造一本鍛造的精通法器湊合,福詞條約等於沒有,因為我沒有祭禮殘章,草神不站場也用不了賭狗,武器的材料好打,直接幹到 \(80\) 級,再把草主的聖遺物直接扒下來給草神用。

這個突破材料很抽象,比如長在懸崖邊要是沒有草神根本拿不到的劫波蓮和用海草行久根本沒法打的無相草(關於我之前沒解鎖那片地圖這件事,早知道他給“綠寶石”就不用打雞哥了),用海哥不附魔的平 A 和行秋的水簾劍磨,但是無相草是我見過最廢物的無相系列的了,菜雞互啄了屬於是,還有蕈獸不能用雷,沒了久岐忍傷害 \(-60\%\),不過這玩意少了這麼多傷害也打的不慢。

刷到最後給我弄到森林樹地圖來了,沒解鎖根本進不去哈哈,不刷了刷不動了,才 \(70\) 級現在。

發現我有海哥和千精久岐忍,所以草神精通不需要特別特別高,好耶。

開學了,沒放夠嗚嗚嗚(>_<)~。

開學後

奇怪,huge 心平氣和的 D 高二遲到的(沒幾個不遲到的),好像一點不生氣,還和我們講了比如之前有人把手機藏到充電寶裡的一些逆天事例。

比賽

掛的快比得的分多了,T1 只篩到 \(m\) 被 hack 哩(全機房都被 hack 了),T2 部分分給了足足 \(80\),那我還想個屁正解直接寫部分分,沒開 long long 掛 \(30\)?!?T4 我會部分分!沒打完……

T1 題面一開始還是錯的,所以先打的 T2,T2 飛快拿到 \(80\) 後去寫 T1,發現二分顯然就開打,然後讀假題了,我甚至在那兒懷疑大樣例是錯的都沒有懷疑自己讀假題了,瞪了 \(2\) 個多小時發現讀假題了,發現加個埃篩就過了。

T2 出線段樹分治板子,記得上次出珂朵莉維護顏色段板子也是部分分給了足足 \(80\)

image

T1 暴力操作

顯然可以二分答案。

因為 \(\lfloor\dfrac{a}{bc}\rfloor=\lfloor\dfrac{\lfloor\frac{a}{b}\rfloor}{b}\rfloor\),對於一個 \((a_i,x)\) 所滿足 \(\lfloor\dfrac{a_i}{x}\rfloor\le mid,x<y\) 一定滿足 \(\lfloor\dfrac{a_i}{y}\rfloor\le mid\),所以先對於 \(c_i\) 做一次字尾 \(\min\),再跑一邊埃篩(類似揹包吧):\(\forall j\mid i,c_{i}=\min(c_i,c_j\times c_{\frac{i}{j}})\),之後再做一次字尾 \(\min\),使每次操作代價最小。

這裡需要注意一個細節,不能只篩到 \(m\),如 \(n=7\),則 \(7\) 可以透過 \(3\times 3\) 替換掉,但此時他是 \(9\),所以 \(9\) 也要篩到,保險起見篩到 \(2m\)

對於 check,先將 \(a\) 按升序排序,對於一個數是中位數的必要條件是 \(\sum\limits_{i=1}^n[a_i\le x]\ge \lfloor\dfrac{n}{2}\rfloor+1\),若滿足就往左二分即可,滿足該條件的最小的一個一定是中位數,對於 \(n\) 為奇數顯然正確,對於 \(n\) 為偶數,考慮中位數等於 \(\dfrac{a_{\frac{n}{2}}+a_{\frac{n}{2}+1}}{2}\),現在顯然有 \(a_{\frac{n}{2}}\le mid\),若能夠使 \(a_{\frac{n}{2}+1}< mid\) 則此時中位數是 \(<mid\) 的,答案還能再小,直到最小的一個滿足的。

具體如何找到一個 \(x\) 滿足 \(\lfloor\dfrac{a_i}{x}\rfloor\le mid\),有 \(\dfrac{a_i}{x}<mid+1\),則 \(x>\dfrac{a_i}{mid+1}\),即 \(x\ge \lfloor\dfrac{a_i}{mid+1}\rfloor+1\),也可以雙指標跑,D 一下部分人二分找 \(x\) 的雙 \(\log\) 做法。

複雜度 \((n+m)\log m\)

點選檢視程式碼
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=5e5+10;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar_unlocked();
	for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
	for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
	x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,k,ans,a[N],b[N<<1];
inline bool check(int x)
{
	int pos=upper_bound(a+1,a+1+n,x)-a-1,res=0;
	if(pos>(n>>1)) return 1;
	for(int i=pos+1;i<=(n>>1)+1;i++) (res+=b[a[i]/(x+1)+1])>k&&(i=n);
	return res<=k;
}
signed main()
{
	freopen("opt.in","r",stdin),freopen("opt.out","w",stdout);
	read(n,m,k);
	for(int i=1;i<=n;i++) read(a[i]); sort(a+1,a+1+n);
	for(int i=1;i<=m;i++) read(b[i]); fill(b+m+1,b+2*m+1,1e9+1);
	for(int i=m-1;i;i--) b[i]=min(b[i],b[i+1]);
	for(int i=1;i<=2*m;i++) for(int j=2*i;j<=2*m;j+=i) b[j]=min(b[j],b[i]+b[j/i]);
	for(int i=2*m-1;i;i--) b[i]=min(b[i],b[i+1]);
	for(int l=0,r=m,mid;l<=r;) check(mid=l+r>>1)?r=(ans=mid)-1:l=mid+1;
	write(ans);
}

T2 異或連通

  • P5787 二分圖 /【模板】線段樹分治

發現每個 \(w_i\) 能夠存在對應的區間不超過 \(\log k\) 段,這個是能夠用 01trie 求出來的。

之後就是線段樹分治板子了,就是線上段樹上跑 dfs 加回溯,用可撤銷並查集維護,複雜度 \(n\log^2 n\)

點選檢視程式碼
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
#define mid (l+r>>1)
#define ls (mid<<1)
#define rs (mid<<1|1)
using namespace std;
const int N=1e5+10;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar_unlocked();
	for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
	for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
	x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int n,m,q,k,top,res,tot=1,u[N],v[N],w[N],a[N],b[N],f[N],fa[N],sz[N],sta[N],ans[N],l[N<<5],r[N<<5],son[N<<5][2];
vector<int>g[N],t[N<<1]; unordered_map<int,int>mp;
inline int find(int x) {while(x!=fa[x]) x=fa[x]; return x;}
inline void merge(int x,int y)
{
	sz[x]>sz[y]&&(x^=y^=x^=y),fa[sta[++top]=x]=y,res-=f[y],res-=f[x];
	res+=(f[y]+=1ll*sz[x]*sz[y]+f[x]),sz[y]+=sz[x];
}
inline void undo(int ltop,int lres)
{
	for(int x,y;top>ltop;top--,f[y]-=1ll*sz[x]*sz[y]+f[x])
		x=sta[top],y=fa[sta[top]],sz[y]-=sz[x],fa[x]=x; res=lres;
}
inline void add(int p,int l,int r,int vl,int vr,int x)
{
	if(vl==0) return ; if(vl<=l&&vr>=r) return t[p].push_back(x),void(0);
	if(vl<=mid) add(ls,l,mid,vl,vr,x); if(vr>mid) add(rs,mid+1,r,vl,vr,x);
}
inline void ask(int p,int l,int r)
{
	int ltop=top,x,y,lres=res;
	for(int i:t[p]) if((x=find(u[i]))!=(y=find(v[i]))) merge(x,y);
	if(l==r) {for(int i:g[l]) ans[i]=res; return undo(ltop,lres),void();}
	ask(ls,l,mid),ask(rs,mid+1,r),undo(ltop,lres);
}
inline void insert(int x,int id)
{
	for(int i=30,p=1,c;~i;i--,r[p=son[p][c]]=id)
		if(!son[p][c=(x>>i)&1]) l[son[p][c]=++tot]=id;
}
inline void ask(int id)
{
	for(int i=30,p=1,c,d,j;~i&&p;i--)
	{
		j=son[p][c=(w[id]>>i)&1],d=(k>>i)&1;
		if(d) add(1,1,b[0],l[j],r[j],id),p=son[p][c^1]; else p=j;
	}
}
signed main()
{
	freopen("xor.in","r",stdin),freopen("xor.out","w",stdout);
	read(n,m,q,k); for(int i=1;i<=n;i++) fa[i]=i,sz[i]=1;
	for(int i=1;i<=m;i++) read(u[i],v[i],w[i]);
	for(int i=1;i<=q;i++) read(a[i]),b[i]=a[i];
	sort(b+1,b+1+q),b[0]=unique(b+1,b+1+q)-b-1;
	for(int i=1;i<=b[0];i++) insert(b[i],mp[b[i]]=i);
	for(int i=1;i<=q;i++) g[mp[a[i]]].push_back(i);
	for(int i=1;i<=m;i++) ask(i); ask(1,1,b[0]);
	for(int i=1;i<=q;i++) write(ans[i]),puts("");
}

T3 詭異鍵盤

沒有太大改的慾望,留最後改吧。

T4 民主投票

\(f_{x,s}\) 表示以 \(x\) 為根的子樹在欽定每個點最大值不超過 \(s\) 的情況下最少給祖先節點貢獻多少票。

考慮二分出一個最小的 \(s\) 滿足 \(f_{1,s}=1\),那麼此時 \(size_x-1>s\) 的答案均為 \(1\)\(size_x-1<s\) 答案均為 \(0\),考慮 \(size_x-1=s\) 的。

考慮對 \(s-1\) 跑一遍 DP,此時顯然有 \(f_{1,s-1}>1\),對於一個 \(x\) 答案為 \(1\),需要滿足 \(1\to x\) 路徑上所有點的 \(f_{i,s-1}=2\),因為此時 \(x\) 節點應有 \(s\) 票,所以可以消掉這一條路徑使其全部為 \(1\),說明合法。發現 \(f_{x,s-1}\le 2\),當 \(f_{x,s-1}=1\) 時答案一定為 \(0\),因為無法把 \(f_{1,s-1}\) 消成 \(1\)

實現是可以做到遞推的,跑的飛快,但是像我這樣遞迴寫的雖然慢但是不用卡常也能過,複雜度 \(O(n\log n)\)

點選檢視程式碼
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e6+10;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar_unlocked();
	for(;!isdigit(c);c=getchar_unlocked()) if(c=='-') z=0;
	for(;isdigit(c);c=getchar_unlocked()) x=(x<<1)+(x<<3)+(c^48);
	x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x){if(x>9)wt(x/10);putchar_unlocked((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar_unlocked('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar_unlocked(' ');write(y...);}
int T,n,f[N],sz[N]; char ans[N]; vector<int>e[N];
inline void dfs(int x) {sz[x]=0; for(int y:e[x]) dfs(y),sz[x]+=sz[y]+1;}
inline bool check(int mid)
{
	auto dp=[&](auto &&dp,int x)->void
	{for(int y:e[x]) dp(dp,y),f[x]+=f[y]; f[x]=max(0,f[x]-mid)+1;};
	return memset(f,0,4*(n+1)),dp(dp,1),f[1]==1;
}
inline void getans(int x,int mid)
{
	if(sz[x]==mid) return ans[x]='1',void();
	for(int y:e[x]) if(f[y]>1) getans(y,mid);
}
signed main()
{
	freopen("election.in","r",stdin),freopen("election.out","w",stdout);
	for(read(T);T;T--)
	{
		read(n); int l=1,r=n,mid,res;
		for(int i=2,x;i<=n;i++) read(x),e[x].push_back(i); dfs(1);
		while(l<=r) check(mid=l+r>>1)?r=(res=mid)-1:l=mid+1;
		for(int i=1;i<=n;i++) ans[i]=sz[i]>res?'1':'0';
		check(res-1); if(f[1]==2) getans(1,res);
		ans[n+1]='\0',printf("%s\n",ans+1);
		for(int i=1;i<=n;i++) vector<int>().swap(e[i]);
	}
}

相關文章