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);
附录

上一次是《蜀道难》,这也是第二次以“穗”命名题目了。
学长之前画的这幅:


浙公网安备 33010602011771号