NOIP2024 前集训:NOIP2024加赛 3(欢乐赛)

前言

再次考古,现在才写。

这场叫欢乐赛,搬的 CF,不知道 OJ 哪儿来的 RMJ。

就记得 T3 一直往数据结构上想浪费好多时间,结果发现是结论题 \(O(n)\) 可做。

T1 Sakurako and Water

虽然我之前不知道主对角线是什么东西,但是看完题目自动把他过滤成左上角到右下角了(不知道当时怎么想的,好像根本没想),然后发现一条对角线每次选都选了不全选是唐氏,就做完了。

T2 Binomial Coefficients, Kind Of

题目是给了一串(错误的)杨辉三角求组合数代码,人均读假一遍题,发现他写的是错的,实际是 \(2^k\)

T3 QED's Favorite Permutation

不知道 QED 和这题啥关系。

发现 LR 会成为分割点使左右不再互通,因为他每次都是单点修改,所以根本不用维护这说直接判就可以了,判断这个 LR 是否有区间需要越过他,前缀和一下即可。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e5+10;
int T,n,m,cant,a[N],suml[N],sumr[N]; string s;
signed main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	for(cin>>T;T;T--,cant=0)
	{
		cin>>n>>m,memset(suml,0,4*(n+1)),memset(sumr,0,4*(n+1));
		for(int i=1,x;i<=n;i++)
			cin>>x,(x!=i)&&(++suml[min(x,i)+1])&&(++sumr[max(x,i)]);
		for(int i=1;i<=n;i++) suml[i]+=suml[i-1],sumr[i]+=sumr[i-1];
		cin>>s,s=" "+s; for(int i=2,tmp;i<=n;i++)
			cant+=(s[i]=='R'&&s[i-1]=='L'&&suml[i]-sumr[i-1]);
		for(int i=1,x;i<=m;i++)
		{
			cin>>x;
			cant-=(s[x]=='L'&&s[x+1]=='R'&&suml[x+1]-sumr[x]);
			cant-=(s[x]=='R'&&s[x-1]=='L'&&suml[x]-sumr[x-1]);
			cant+=(s[x]=='L'&&s[x-1]=='L'&&suml[x]-sumr[x-1]);
			cant+=(s[x]=='R'&&s[x+1]=='R'&&suml[x+1]-sumr[x]);
			puts(cant?"NO":"YES"),s[x]=s[x]=='R'?'L':'R';
		}
	}
}

T4 Card Game

是道不需要一点优化的朴素 DP,但赛时一直唐 T3 没有写出来。

发现对于 \(1\) 花色的牌只有 Alice 可以多拿,同理剩下的牌只有 Bob 可以多拿,且 Alice 多拿的数量等于 Bob 多拿的数量。

\(f_{i,j}\) 表示在花色为 \(1\) 的排中,目前为第 \(i\) 大的牌,Alice 多拿了 \(j\) 张的方案数,显然有转移:

\[f_{i,j}=f_{i-1,j-1}+f_{i-1,j+1} \]

\(g_{i,j}\) 表示在不包括 \(1\) 的前 \(i\) 种花色中 Bob 多拿了 \(j\) 张牌的贡献,发现对于每一种花色的转移和上面是一样的,所以有:

\[g_{i,j}=\sum_{k=0}^jg_{i-1,k}f_{m,j-k} \]

根据上面的结论,最后答案就是 \(\sum\limits_{i=0}^mf_{m,i}g_{n-1,i}\)

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=510,P=998244353;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') z=0;
	for(;isdigit(c);c=getchar()) 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((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar(' ');write(y...);}
int n,m,ans,f[N][N],g[N][N];
inline int mod(int x,int y) {return (x+=y)>=P?x-P:x;}
signed main()
{
	read(n,m),n--,f[0][0]=1;
	for(int i=1;i<=m;f[i][0]=f[i-1][1],i++) for(int j=1;j<=m;j++)
		f[i][j]=mod(f[i-1][j-1],f[i-1][j+1]);
	g[0][0]=1; for(int i=1;i<=n;i++) for(int j=0;j<=m;j++)
		for(int k=0;k<=j;k++) g[i][j]=mod(g[i][j],1ll*f[m][k]*g[i-1][j-k]%P);
	for(int i=0;i<=m;i++) ans=mod(ans,1ll*f[m][i]*g[n][i]%P);
	write(ans);
}

T5 Long Way to be Non-decreasing

每次选一个集合就是说直接对每个点计算步数,最后取最大值就是答案,所以发现可以二分答案,表示是否能在 \(mid\) 步内走完,check 可以双指针跑。

考虑怎么计算距离,\(i\to b_i\) 连边,发现这是一棵基环树森林,所以不在一棵树上的距离为 \(\inf\),否则计算断边前后的距离取最小值即可。

点击查看代码
#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();
	for(;!isdigit(c);c=getchar()) if(c=='-') z=0;
	for(;isdigit(c);c=getchar()) 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((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar(' ');write(y...);}
int T,n,m,tot,ans=-1,a[N],b[N],f[N],in[N],out[N],del[N],dep[N]; vector<int>e[N];
inline int find(int x) {return x==f[x]?x:f[x]=find(f[x]);}
inline void dfs(int x)
{in[x]=++tot; for(int y:e[x]) dep[y]=dep[x]+1,dfs(y); out[x]=tot;}
inline bool insub(int x,int y) {return in[x]<=in[y]&&out[x]>=out[y];}
inline int getdis(int x,int y)
{
	if(find(x)!=find(y)) return 1e9;
	if(insub(y,x)) return dep[x]-dep[y];
	if(insub(y,b[del[find(x)]])) return dep[x]+dep[b[del[find(x)]]]-dep[y]+1;
	return 1e9;
}
inline bool check(int x)
{
	for(int i=1,j=1;i<=n;i++)
	{while(j<=m&&getdis(a[i],j)>x) j++; if(j>m) return 0;}
	return 1;
}
signed main()
{
	for(read(T);T;T--,tot=0,ans=-1)
	{
		read(n,m),memset(del,0,4*(m+1)),memset(dep,0,4*(n+1));
		for(int i=1;i<=n;i++) read(a[i]);
		for(int i=1;i<=m;i++) read(b[i]),f[i]=i,vector<int>().swap(e[i]);
		for(int i=1,x,y;i<=m;i++)
		{
			if((x=find(i))==(y=find(b[i]))) del[x]=i;
			else f[x]=y,e[b[i]].push_back(i);
		}
		for(int i=1;i<=m;i++) if(f[i]==i) dfs(del[i]);
		for(int l=0,r=m,mid;l<=r;)
		{if(check(mid=l+r>>1)) ans=mid,r=mid-1; else l=mid+1;}
		write(ans),puts("");
	}
}

T6 Many Games

考虑如何 DP,这是简单的,设 \(f_{j}\) 表示和为 \(j\) 的概率,01 背包直接做,但是发现复杂度是错误的,现在需要考虑怎么证明其复杂度是正确的。

首先对于 \(p=100\) 的一定都选上,否则对于所有 \(p\) 相等的,设其选 \(k\) 个最优,则当然是选 \(w\) 最大的 \(k\) 个,设 \(s=\sum\limits_{i=1}^k w_i,mn=\min\limits_{i=1}^kw_i\),则有:

\[(p\%)^ks\ge (p\%)^{k-1}(s-mn)\ge (p\%)^{k-1}\dfrac{k-1}{k}s \]

从而得到 \(k\le \lfloor\frac{1}{1-p\%}\rfloor=\lfloor\frac{100}{100-p}\rfloor\),从而选出的总数 \(\le \sum\limits_{i=1}^{99}\lfloor\frac{100}{100-p}\rfloor=481\)

现在还有一维 \(\sum w\) 还是很大,设 \(s=\sum w,t=\prod p\),根据最优性,去掉一个 \((p,w)\) 一定不优,有:

\[t\times s\ge \frac{t}{p\%}(s-w) \]

\[s\le \frac{w}{1-p\%} \]

又因为 \(w\times p\le 2\times 10^5\),所以有:

\[s\le \frac{\frac{2\times 10^5}{p}}{1-p\%}=\frac{2\times 10^7}{p(100-p)} \]

\(p=1\)\(99\) 时取到最大,即 \(s\le 202020\),从而最初那个 DP 的复杂度便变成了正确的。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int mx=202020;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') z=0;
	for(;isdigit(c);c=getchar()) 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((x%10)+'0');}
template<typename Tp> inline void write(Tp x){if(x<0)putchar('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar(' ');write(y...);}
int n,sum; double ans,f[mx+5]; priority_queue<int>q[100];
signed main()
{
	read(n),f[0]=1; for(int i=1,x,y;i<=n;i++)
	{read(x,y); if(x==100) sum+=y; else q[x].push(y);}
	for(int i=1;i<=99;i++) for(int j=100/(100-i),x;j&&q[i].size();j--)
	{
		x=q[i].top(),q[i].pop();
		for(int k=mx;k>=x;k--) f[k]=max(f[k],f[k-x]*i/100.0);
	}
	for(int i=0;i<=mx;i++) ans=max(ans,(i+sum)*f[i]);
	printf("%.8lf",ans);
}
posted @ 2024-11-13 16:02  卡布叻_周深  阅读(26)  评论(2编辑  收藏  举报