2024暑假集训测试24

前言

感觉好不在状态啊,T1 没搞出来,T4 少了个离散化挂了 \(60\)

T1 Kanon

签到题是个蓝,但不该做不出来。

考虑一个球不可能吃到所有其右面球再往右的雪,反之同理。

那么处理出每个店最左往左走多少,最右往右走多少步,这个显然有单调性,考虑二份答案处理出相邻两点第一次存在交集时那个交集属于谁。

点击查看代码
#include<bits/stdc++.h>
#define ll long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e5+10;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar();
	for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
	for(;'0'<=c&&c<='9';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;
ll sum,ans[N],a[N],b[N],le[N],ri[N];
void solve(int l,int r,int x,int y,ll len)
{
	if(ri[m]+le[m]<len) 
	{
		ans[x]+=ri[m];
		ans[y]+=le[m];
		return ;
	}
	int pos,mid;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(ri[mid]+le[mid]>=len) pos=mid,r=mid-1;
		else l=mid+1;
	}
	if(ri[pos]==ri[pos-1]) ans[x]+=ri[pos],ans[y]+=len-ri[pos];
	else ans[y]+=le[pos],ans[x]+=len-le[pos];
}
signed main()
{
	read(n,m);
	for(int i=1;i<=n;i++) read(a[i]),b[i]=a[i]-a[i-1];
	for(int i=1;i<=m;i++)
	{
		ll x;
		read(x); sum+=x;
		le[i]=max(le[i-1],-sum);
		ri[i]=max(ri[i-1],sum);
	}
	ans[1]+=le[m],ans[n]+=ri[m];
	for(int i=2;i<=n;i++) solve(1,m,i-1,i,b[i]);
	for(int i=1;i<=n;i++) write(ans[i]),puts("");
}

T2 Summer Pockets

若行间建 \(x\) 条,列间建 \(y\) 条,单从行考虑,分割出的每个块内蝴蝶数量应为 \(2y\),单从列考虑,分割出的每个块内蝴蝶数量应为 \(2x\)

行间列间分别处理前缀和,通过上面性质,再枚举 \(x,y\),每组 \(x,y\) 根据上面的性质处理,分出联通块后每个块二维前缀和判断是否合法,发现存在没有蝴蝶的行或列,从而使一个这栏可以放到不同位置,这些之间乘法原理乘起来,最后加法原理加起来即可,因为 \(2xy=tot\),所以这么枚举复杂度是真的。

点击查看代码
#include<bits/stdc++.h>
#define ll long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2010,P=998244353;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar();
	for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
	for(;'0'<=c&&c<='9';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,tot,ans,c1,c2,s1[N],s2[N],p1[N],p2[N],cnt1[N],cnt2[N],sum[N][N];
char a[N][N];
int ask(int x1,int y1,int x2,int y2)
{
	return sum[x2][y2]-sum[x1][y2]-sum[x2][y1]+sum[x1][y1];
}
void solve(int x,int y)
{
	int c1=0,c2=0,res=1;
	for(int i=1;i<=n;i++) 
		if(s1[i]!=s1[i-1]&&s1[i]%x==0) p1[++c1]=i;
	for(int i=1;i<=m;i++) 
		if(s2[i]!=s2[i-1]&&s2[i]%y==0) p2[++c2]=i;
	if(c1<<1!=y||c2<<1!=x) return ;
	for(int i=1;i<=c1;i++)
		for(int j=1;j<=c2;j++)
			if(ask(p1[i-1],p2[j-1],p1[i],p2[j])!=2) return ;
	for(int i=1;i<=max(c1,c2);i++) cnt1[i]=cnt2[i]=0;
	for(int i=1;i<=n;i++) 
		if(s1[i]%x==0) cnt1[s1[i]/x]++;
	for(int i=1;i<=m;i++)
		if(s2[i]%y==0) cnt2[s2[i]/y]++;
	for(int i=1;i<c1;i++) res=1ll*res*cnt1[i]%P;
	for(int i=1;i<c2;i++) res=1ll*res*cnt2[i]%P;
	ans+=res; ans=ans>=P?ans-P:ans;
}
signed main()
{
	read(n,m);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",a[i]+1);
		for(int j=1;j<=m;j++)
		{
			sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
			if(a[i][j]=='Y') s1[i]++,s2[j]++,sum[i][j]++;
		}
	}
	for(int i=1;i<=n;i++) s1[i]+=s1[i-1];
	for(int i=1;i<=m;i++) s2[i]+=s2[i-1];
	tot=s1[n];
	if(tot&1) puts("0"),exit(0);
	for(int i=2;i<=tot;i+=2) if(tot%i==0) solve(i,2*tot/i);
	write(ans);
}

T3 空之境界

  • 原题:QOJ Deleting

  • 部分分 \(60pts\)\(O(n^3)\) DP,本来应该是 \(40pts\)

  • 正解,二分答案:

    首先有 DP 式子:

    \[f_{l,r}=\min(\max(f_{l+1,r-1},a_{l,r}),\max(f_{l,k},f_{k+1,r})) \]

    考虑二分答案,只有 \(a_{l,r}\le mid\) 的才可以转移,那么 \(f_{l,r}\) 就可以用 bitset 压起来,复杂度 \(O(\dfrac{n^3\log n}{w})\) 貌似没有暴力复杂度优多少?不,你要相信 bitset,这不仅没是 QOJ、学校 OJ 上最优解,而且比 \(O(\dfrac{n^3}{w})\) 正解快两倍不止,CF 的数据都跑的飞快!

    但这里需要一个优化,对于 \(f_{l,r}\)\(f_{l+1,r-1}\) 转移过来时,考虑此时 \(r\) 变成了上面的 \(k\),于是让 \(f_l,f_{r+1}\) 进行按位或,同时考虑其传递性,若在转移之前 \(f_{l,r}\) 就为 \(1\),则不进行上述操作,这也是其跑得飞快的根本原因(不加也能过其实)。

    • QOJ 上 \(200\)\(ms\),学校 OJ 上 \(2000\) 多,CF \(1400\) 多,目前视野范围内均为最优解。
    点击查看代码
    #include<bits/stdc++.h>
    #define ll long long 
    #define endl '\n'
    #define sort stable_sort
    using namespace std;
    const int N=4010;
    template<typename Tp> inline void read(Tp&x)
    {
    	x=0;register bool z=true;
    	register char c=getchar();
    	for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    	for(;'0'<=c&&c<='9';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,a[N][N];
    bitset<N>f[N];
    bool check(int mid)
    {
    	for(int i=1;i<=n;i++) f[i].reset(),f[i][i-1]=1;
    	for(int l=n-1;l>=1;l--)
    		for(int r=l+1;r<=n;r+=2)
    			if(f[l+1][r-1]&&!f[l][r]&&a[l][r]<=mid) 
    				f[l][r]=1,f[l]|=f[r+1];
    	return f[1][n];
    }
    signed main()
    {
    	read(n);
    	for(int i=1;i<=n;i++) 
    		for(int j=i+1;j<=n;j+=2)
    			read(a[i][j]);
    	int l=1,r=n*n>>2,ans;
    	while(l<=r)
    	{
    		int mid=(l+r)>>1;
    		if(check(mid)) r=mid-1,ans=mid;
    		else l=mid+1;
    	}
    	write(ans);
    }
    
  • 正解(真):

    反正没有二分跑得快就是了,改为枚举答案上限,每次 \(i\to i+1\)\(i+1\) 产生的贡献都加上,每个点的都只会被更新一次,故复杂度为 \(O(\dfrac{n^3}{w})\)

    甚至学这玩意还学了个 bitset 的科技。

    点击查看代码
    #include<bits/stdc++.h>
    #define ll long long 
    #define endl '\n'
    #define sort stable_sort
    using namespace std;
    const int N=4010;
    template<typename Tp> inline void read(Tp&x)
    {
    	x=0;register bool z=true;
    	register char c=getchar();
    	for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    	for(;'0'<=c&&c<='9';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,a[N][N];
    bitset<N>f[N],g[N];
    pair<int,int>pos[N*N>>2];
    void solve(int l,int r,int x)
    {
    	if(f[l][r]) return ;
    	f[l][r]=g[r][l]=1;
    	if(l>1&&r<n&&a[l-1][r+1]<=x) solve(l-1,r+1,x);
    	auto t=(f[l]|f[r+1])^f[l];
    	for(int i=t._Find_first();i<=n;i=t._Find_next(i)) solve(l,i,x);
    	t=(g[r]|g[l-1])^g[r];
    	for(int i=t._Find_first();i<=n;i=t._Find_next(i)) solve(i,r,x);
    }
    signed main()
    {
    	read(n);
    	for(int i=1;i<=n;i++) 
    	{
    		for(int j=i+1;j<=n;j+=2)
    		{
    			read(a[i][j]);
    			pos[a[i][j]]=make_pair(i,j);
    		}
    		f[i][i-1]=1;
    	}
    	for(int i=1;i<=n*n/4;i++)
    	{
    		int l=pos[i].first,r=pos[i].second;
    		if(f[l+1][r-1]) solve(l,r,i);
    		if(f[1][n]) write(i),exit(0);
    	}
    }
    

T4 穗

  • 总共分了 \(4\) 档分,部分分给的很足,足足 \(80pts\),最后 \(20pts\) 对应原题:P4690 [Ynoi2016] 镜中的昆虫

  • 第一档 \(20pts\):暴力即可。

  • 第二档 \(20pts\):没有修改,普通莫队直接冲,或者主席树、分块等一系列做法。

  • 第三档 \(40pts\):单点修改,带修莫队直接冲,或者主席树、分块等一系列做法。

  • 正解:涉及颜色段均摊等操作,即将区间修改通过某种方式转换为一段一段的单点修改,再跑一边 cdq 等东西求一下,这个板子巨难打,所以还不会。

80pts 代码
#include<bits/stdc++.h>
#define ll long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e5+10;
template<typename Tp> inline void read(Tp&x)
{
	x=0;register bool z=true;
	register char c=getchar();
	for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
	for(;'0'<=c&&c<='9';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,a[N],b[N],cnt[N];
struct aa {int op,l,r,x;}g[N];
struct small
{
	void solve()
	{
		for(int i=1,op,l,r,x;i<=m;i++)
		{
			op=g[i].op,l=g[i].l,r=g[i].r;
			if(op==1) 
			{
				x=g[i].x;
				for(int j=l;j<=r;j++) a[j]=x;
			}
			else
			{
				int sum=0;
				for(int j=l;j<=r;j++) cnt[a[j]]=0;
				for(int j=l;j<=r;j++) 
				{
					cnt[a[j]]++;
					sum+=(cnt[a[j]]==1);
				}
				write(sum),puts("");
			} 
		}
	}
}SM;
struct normal_mo
{
	int sum,pos[N],ans[N];
	struct bb {int l,r,id;}e[N];
	void add(int x) {cnt[x]++; sum+=(cnt[x]==1);}
	void del(int x) {cnt[x]--; sum-=(cnt[x]==0);}
	void solve()
	{
		int T=sqrt(n);
		for(int i=1;i<=n;i++) pos[i]=(i-1)/T+1;
		for(int i=1;i<=m;i++) e[i]={g[i].l,g[i].r,i};
		sort(e+1,e+1+m,[=](bb a,bb b){return pos[a.l]==pos[b.l]?((pos[a.l]&1)?a.r<b.r:a.r>b.r):a.l<b.l;});
		for(int i=1,l=1,r=0;i<=m;i++)
		{
			while(l<e[i].l) {del(a[l]); l++;}
			while(l>e[i].l) {l--; add(a[l]);}
			while(r<e[i].r) {r++; add(a[r]);}
			while(r>e[i].r) {del(a[r]); r--;}
			ans[e[i].id]=sum;
		}
		for(int i=1;i<=m;i++) write(ans[i]),puts("");
	}
}NO;
struct change_mo
{
	int sum,tot_c,tot_q,l,r,t,pos[N],now[N],ans[N];
	struct cc {int pos,nw,old;}c[N];
	struct qq {int l,r,t,id;}q[N];
	void add(int x) {cnt[x]++; sum+=(cnt[x]==1);}
	void del(int x) {cnt[x]--; sum-=(cnt[x]==0);}
	void change(int x,int d)
	{
		if(l<=x&&r>=x) del(a[x]),add(d);
		a[x]=d;
	}
	void solve()
	{
		int T=pow(n,0.6667);
		for(int i=1;i<=n;i++) 
		{
			now[i]=a[i];
			pos[i]=(i-1)/T+1;
		}
		for(int i=1,op,l,r,x;i<=m;i++) 
		{
			op=g[i].op,l=g[i].l,r=g[i].r;
			if(op==1)
			{
				x=g[i].x;
				c[++tot_c]={l,x,now[l]};
				now[l]=x;
			}
			else q[++tot_q]={l,r,tot_c,tot_q};
		} 
		sort(q+1,q+1+tot_q,[=](qq a,qq b){return pos[a.l]==pos[b.l]?(pos[a.r]==pos[b.r]?a.t<b.t:a.r<b.r):a.l<b.l;});
		l=1,r=0,t=0;
		for(int i=1;i<=tot_q;i++)
		{
			while(t<q[i].t) {t++; change(c[t].pos,c[t].nw);}
			while(t>q[i].t) {change(c[t].pos,c[t].old); t--;}
			while(l<q[i].l) {del(a[l]); l++;}
			while(l>q[i].l) {l--; add(a[l]);}
			while(r<q[i].r) {r++; add(a[r]);}
			while(r>q[i].r) {del(a[r]); r--;}
			ans[q[i].id]=sum;
		}
		for(int i=1;i<=tot_q;i++) write(ans[i]),puts("");
	}
}CH;
signed main()
{
	read(n,m);
	for(int i=1;i<=n;i++) read(a[i]),b[i]=a[i];
	bool flag1=0,flag2=0;
	for(int i=1,op,l,r,x;i<=m;i++)
	{
		read(op,l,r);
		if(l>r) swap(l,r);
		flag1|=(op==1);
		flag2|=(op==1&&l!=r);
		if(op==1) read(x),g[i]={op,l,r,x},b[i+n]=x;
		else g[i]={op,l,r,0};
	}
	sort(b+1,b+1+n+m);
	b[0]=unique(b+1,b+1+n+m)-(b+1);
	for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+b[0],a[i])-b;
	for(int i=1;i<=m;i++) g[i].x=lower_bound(b+1,b+1+b[0],g[i].x)-b;
	if(n<=5000&&m<=5000) SM.solve();
	else if(!flag1) NO.solve();
	else if(!flag2) CH.solve();
	else SM.solve();//I'm so sorry.
}

总结

虽然挂分挂惨了但是学到了一些有用的科技。

  • struct 封装直接调用其内部定义的数组排序会编译错误,若使用 lamda 表达式要在中括号中加一个 \(=\) 表示引用(jijidawang 教的)。

    sort(e+1,e+1+m,[=](bb a,bb b){return pos[a.l]==pos[b.l]?((pos[a.l]&1)?a.r<b.r:a.r>b.r):a.l<b.l;});
    
  • bitset 自带一个“找 \(1\)” 的函数:

    for(int i=t._Find_first();i<=n;i=t._Find_next(i)) solve(l,i,x);
    

附录

image

上一次是《蜀道难》,这也是第二次以“穗”命名题目了。

学长之前画的这幅:

image

posted @ 2024-08-15 07:26  卡布叻_周深  阅读(83)  评论(1)    收藏  举报