NOIP2024 前集训:NOIP2024加赛 5(兼假期闲话)
前言
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\) 级现在。
发现我有海哥和千精久岐忍,所以草神精通不需要特别特别高,好耶。
开学了,没放够呜呜呜 \(\sim\sim\sim\sim(>\_<)\sim\sim\sim\sim\)。
开学后
奇怪,huge 心平气和的 D 高二迟到的(没几个不迟到的),好像一点不生气,还和我们讲了比如之前有人把手机藏到充电宝里的一些逆天事例。
比赛
挂的快比得的分多了,T1 只筛到 \(m\) 被 hack 哩(全机房都被 hack 了),T2 部分分给了足足 \(80\),那我还想个屁正解直接写部分分,没开 long long 挂 \(30\)?!?T4 我会部分分!没打完……
T1 题面一开始还是错的,所以先打的 T2,T2 飞快拿到 \(80\) 后去写 T1,发现二分显然就开打,然后读假题了,我甚至在那儿怀疑大样例是错的都没有怀疑自己读假题了,瞪了 \(2\) 个多小时发现读假题了,发现加个埃筛就过了。
T2 出线段树分治板子,记得上次出珂朵莉维护颜色段板子也是部分分给了足足 \(80\)。
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 异或连通
发现每个 \(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]);
}
}