首页 > 其他分享 >CSP2024 前集训:NOIP2024加赛 1

CSP2024 前集训:NOIP2024加赛 1

时间:2024-11-05 21:19:48浏览次数:3  
标签:unlocked int void CSP2024 Tp read inline 加赛 NOIP2024

前言

image

赛时本来 rk3,赛后自己加 hack 卡自己于是成 rk4 了。

因为这场是假做法大战,T1 假贪心有 \(92pts\);T2 \(O(n^2m)\) 能过(是因为数据里 \(m\le 10\));

T3 相当抽象,赛时我打的爆搜只加了一个剪枝能得 \(70pts\),赛后发现无解的时候会跑满,于是提前判掉无解就过了甚至最优解 \(30ms\),发现数据里的数据生成器好像是错的?\(m\) 全都很小,但是发现 \(m\) 很大的时候基本上直接输出最短路就是答案,所以造一组 \(m\) 不大不小的就卡掉了;

然后我想了一个假做法,刚开始我还以为是真的还上去讲,结果大家全都跟着打假做法和爆搜,经过与 wang54321 等人的分析发现是假的,于是造了好长时间的 hack,刚开始不知道怎么造才能完全掉随机打乱,发现及时随机打乱原来能错的仍有不小的错误率,所以直接多随几组能错的绑包就好了。

然后就有了菜等人数据点分治过 hack(因为 hack 数据不是很大),丁真研发新的假做法继续过 hack,xrlong 发现我的数据有自环(我记得我判了呀不是),遂重造 hack,现在除了正解都死了。

本来以为 T3 已经是假做法巅峰了,结果下午发现 T4 目前没有任何一个人打的是正解……

因为 &&|| 有短路,T4 暴力先判并查集比先判是否有交快得多(因为并查集常数小,而 check 全是 if 常数巨大)。

T1 玩游戏

  • 部分分 \(24pts\):\(O(n^2)\) 区间 DP。

  • 正解:

    贪心,考虑如果移动一次断点不可能只移动到一个更大的位置就停了,只要移一定移到更小的位置,所以可以处理处 next。

    发现跳到最小值位置就停了,考虑再反正跳一遍就好了,跳的过程中判是否能安全走完更大的那部分即可,复杂度 \(O(n)\)。

    点击查看代码
    #include<bits/stdc++.h>
    #define ll long long
    #define endl '\n'
    #define sort stable_sort
    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 T,n,m,cnt1,cnt2,nxt1[N],nxt2[N]; 
    ll a[N],b[N],c[N],pre[N],suf[N]; bool flag;
    signed main()
    {
        for(read(T);T;T--,memset(nxt1,0,4*(n+1)),memset(nxt2,0,4*(n+1)))
        {
            read(n,m); for(int i=1;i<=n;i++) read(c[i]);
            a[cnt1=1]=0; for(int i=m;i>=2;i--) a[++cnt1]=c[i];
            b[cnt2=1]=0; for(int i=m+1;i<=n;i++) b[++cnt2]=c[i];
            for(int i=1;i<=cnt1;i++) pre[i]=pre[i-1]+a[i];
            for(int i=1;i<=cnt2;i++) suf[i]=suf[i-1]+b[i];
            if(pre[cnt1]+suf[cnt2]>0) {puts("No"); continue;}
            for(int i=2,x=1;i<=cnt1;i++) if(pre[i]<pre[x]) nxt1[x]=i,x=i;
            for(int i=cnt1-1,x=cnt1;i;i--) if(pre[i]<pre[x]) nxt1[x]=i,x=i;
            for(int i=2,x=1;i<=cnt2;i++) if(suf[i]<suf[x]) nxt2[x]=i,x=i;
            for(int i=cnt2-1,x=cnt2;i;i--) if(suf[i]<suf[x]) nxt2[x]=i,x=i;
            for(int x=1,y=1;nxt1[x]||nxt2[y];flag=0)
            {
                for(int i=x+1;i<=nxt1[x];i++) if(pre[i]+suf[y]>0) {flag=1; break;}
                if(!(flag|=(!nxt1[x]))) {x=nxt1[x]; continue;} flag=0;
                for(int i=y+1;i<=nxt2[y];i++) if(pre[x]+suf[i]>0) {flag=1; break;}
                if(flag|=(!nxt2[y])) break; y=nxt2[y];
            }
            if(flag) {flag=0,puts("No"); continue;}
            for(int x=cnt1,y=cnt2;nxt1[x]||nxt2[y];flag=0)
            {
                for(int i=x-1;i>=nxt1[x];i--) if(pre[i]+suf[y]>0) {flag=1; break;}
                if(!(flag|=(!nxt1[x]))) {x=nxt1[x]; continue;} flag=0;
                for(int i=y-1;i>=nxt2[y];i--) if(pre[x]+suf[i]>0) {flag=1; break;}
                if(flag|=(!nxt2[y])) break; y=nxt2[y];
            }
            puts(flag?"No":"Yes"),flag=0;
        }
    }
    

T2 排列

  • 部分分 \(50pts\):爆搜,发现 \(k=1\) 时为 \(2^{n-1}\)。

  • 正解:

    逆天 DP,感觉不是我赛时能想出来的。

    \(f_{i,j,0/1,0/1}\) 表示长度为 \(i\) 的区间能在不超过 \(j\) 次操作后仅剩最大值的方案数,\(0/1\) 表示左/右端是否存在比区间最大值更大的值。

    转移考虑在两个区间的中间加入一个最大值 \(k\) 的贡献:

    • \(k\) 为整个序列最大值:

      \[f_{i,j,0,0}=\sum\limits_{k=1}^iC_{i-1}^{k-1}\times f_{k-1,j,0,1}\times f_{i-k,j,1,0} \]

    • \(k\) 为右区间最大值,左区间左侧还有更大值,左区间需要留一次操作消最大值 \(k\):

      \[f_{i,j,1,0}=\sum\limits_{k=1}^iC_{i-1}^{k-1}\times f_{k-1,j-1,1,1}\times f_{i-k,j,1,0} \]

    • \(k\) 位左区间最大值,右区间右侧还有更大值,同理:

      \[f_{i,j,0,1}=\sum\limits_{k=1}^iC_{i-1}^{k-1}\times f_{k-1,j,0,1}\times f_{i-k,j-1,1,1} \]

    • 左右区间两侧都有更大值,左右任意区间在 \(j-1\) 内消完即可,去掉两边都是 \(j\) 的贡献:

      \[f_{i,j,1,1}=\sum\limits_{k=1}^iC_{i-1}^{k-1}\times [f_{k-1,j,1,1}\times f_{i-k,j,1,1}-(f_{k-1,j,1,1}-f_{i-k,j-1,1,1})\times (f_{i-k,j,1,1}-f_{i-k,j-1,1,1})] \]

    最后答案为 \(f_{n,n,0,0}\),发现每次操作最少消除 \(\dfrac{n}{2}\) 个点,所以合法的 \(m\le \log_2(n)\),复杂度 \(O(n^2m)\) 实则为 \(O(n^2\log n)\) 级别,需特判不合法的 \(m\)。

    点击查看代码
    #include<bits/stdc++.h>
    #define ll long long
    #define endl '\n'
    #define sort stable_sort
    using namespace std;
    const int N=1010;
    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,P,c[N][N],f[N][11][2][2];
    inline int mod(int x,int y) {return (x+=y)>=P?x-P:x;}
    signed main()
    {
        read(n,m,P); if(m>10) return write(0),0;
        for(int i=0;i<=n;i++) c[i][0]=1;
        for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) 
            c[i][j]=mod(c[i-1][j],c[i-1][j-1]);
        for(int i=0;i<=m;i++) 
            f[0][i][0][0]=f[0][i][0][1]=f[0][i][1][0]=f[0][i][1][1]=1;
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int k=1;k<=i;k++)
        {
            f[i][j][0][0]=mod(f[i][j][0][0],1ll*f[k-1][j][0][1]*f[i-k][j][1][0]%P*c[i-1][k-1]%P);
            f[i][j][1][0]=mod(f[i][j][1][0],1ll*f[k-1][j-1][1][1]*f[i-k][j][1][0]%P*c[i-1][k-1]%P);
            f[i][j][0][1]=mod(f[i][j][0][1],1ll*f[k-1][j][0][1]*f[i-k][j-1][1][1]%P*c[i-1][k-1]%P);
            f[i][j][1][1]=mod(f[i][j][1][1],1ll*(1ll*f[k-1][j][1][1]*f[i-k][j][1][1]%P-1ll*(f[k-1][j][1][1]-f[k-1][j-1][1][1])*(f[i-k][j][1][1]-f[i-k][j-1][1][1])%P+P)%P*c[i-1][k-1]%P);
        }
        write(mod(f[n][m][0][0],P-f[n][m-1][0][0]));
    }
    

T3 最短路

  • 部分分 \(70pts\sim 99pts\):爆搜加剪枝。

  • 部分分 \(99pts\):丁真的假做法。

  • 部分分 \(99pts\):建反图,\(dis_{i,j}\) 表示从 \(1\) 沿正边跑到 \(i\) 加上沿反边跑到 \(j\) 的距离和,那么答案就是 \(dis_{n,n}\),bitset 压状态判有没有走过一个点,跑二维 dijkstra。最短路径不唯一的时候就死了,复杂度 \(O(m\log m)\)。

  • 正解:

    考虑 \(n\to 1\) 的路径一定是沿着 \(1\to n\) 的路径走一段,再沿着不是 \(1\to n\) 的路径走一段,如此往复,称这一段的起点为 A 类点,终点为 B 类点,那么 A 和 B 一定交错出现,\(f_{i,j}\) 表示 \(i\) 为当前最后一个 A,\(j\) 为当前最后一个 B,\(g_{i,j}\) 类似的表示 \(i,j\) 均为 A,那么对于 \(f\) 枚举下一个 A,\(g\) 枚举中间的 A,发现又可以二维 dijkstra 了,复杂度 \(O(n^3\log n)\)。

    点击查看代码
    #include<bits/stdc++.h>
    #define ll long long
    #define endl '\n'
    #define sort stable_sort
    using namespace std;
    const int N=255;
    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,a[N],dis[N][N],f[2][N][N]; bool vis[2][N][N];
    struct aa
    {
        int op,x,y,w;
        inline bool operator < (const aa a) const {return w>a.w;}
    }; priority_queue<aa>q;
    signed main()
    {
        read(n,m),memset(f,0x3f,sizeof(f)),memset(dis,0x3f,sizeof(dis));
        for(int i=1;i<=n;i++) read(a[i]),dis[i][i]=a[i];
        for(int i=1,x,y;i<=m;i++) read(x,y),dis[x][y]=min(dis[x][y],a[x]+a[y]);
        for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) if(dis[i][k]!=0x3f3f3f3f)
            for(int j=1;j<=n;j++) if(dis[k][j]!=0x3f3f3f3f)
                dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]-a[k]);
        for(f[0][1][1]=a[1],q.push({0,1,1,a[1]});!q.empty();)
        {
            aa it=q.top(); q.pop(); int op=it.op,x=it.x,y=it.y;
            if(vis[op][x][y]) continue; vis[op][x][y]=1;
            if(!op) for(int z=1,w;z<=n;z++)
            {
                w=f[0][x][y]+dis[y][z]-a[y]-(x==z&&x!=y)*a[z];
                if(f[1][x][z]>w) f[1][x][z]=w,q.push({1,x,z,w});
            }
            else for(int z=1,w;z<=n;z++)
            {
                w=f[1][x][y]+dis[y][z]+dis[z][x]-a[x]-a[y]-a[z]+(x==z&&x==y)*a[z];
                if(f[0][y][z]>w) f[0][y][z]=w,q.push({0,y,z,w});
            }
        }
        write(f[0][n][n]==0x3f3f3f3f?-1:f[0][n][n]);
    }
    

T4 矩形

写一个错误的解法吧,复杂度貌似能卡,随机数据下应该是 \(O(n\log 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,ans,f[N],up[N<<1],val[N<<1],now[N<<1],add[N<<1];
struct aa {int x1,y1,x2,y2;}e[N];
inline int find(int x) {return x==f[x]?x:f[x]=find(f[x]);}
inline void pushup(int p,int l,int r)
{
	if(val[ls]==2||val[rs]==2) val[p]=2;
	else if(now[ls]==now[rs]) now[p]=now[ls],up[p]=up[ls],val[p]=1;
	else val[p]=2;
}
inline void spread(int p,int l,int r)
{add[ls]=add[rs]=add[p],now[ls]=now[rs]=now[p],up[ls]=up[rs]=up[p],add[p]=0;}
inline void change(int p,int l,int r,int vl,int vr,int d,int id)
{
	if(vl<=l&&vr>=r&&val[p]==1) 
		return (up[p]<d)&&(up[p]=d)&&(now[p]=id)&&(add[p]=1),void();
	if(add[p]) spread(p,l,r);
	if(vl<=mid) change(ls,l,mid,vl,vr,d,id);
	if(vr>mid) change(rs,mid+1,r,vl,vr,d,id);
	pushup(p,l,r);
}
inline void ask(int p,int l,int r,int vl,int vr,int d,int id)
{
	if(vl<=l&&vr>=r&&val[p]==1)
		return (up[p]>=d)&&(f[find(id)]=find(now[p])),void();
	if(add[p]) spread(p,l,r);
	if(vl<=mid) ask(ls,l,mid,vl,vr,d,id);
	if(vr>mid) ask(rs,mid+1,r,vl,vr,d,id);
}
signed main()
{
	read(n); for(int i=1;i<=n;i++) f[i]=i;
	for(int i=1,a,b,c,d;i<=n;i++) read(a,b,c,d),e[i]={a,b,c,d},m=max({m,b,d});
	fill(val+1,val+1+(m<<1),1);
	sort(e+1,e+1+n,[](aa a,aa b){return a.x1==b.x1?a.x2<b.x2:a.x1<b.x1;});
	for(int i=1;i<=n;i++)
	{
		ask(1,1,m,e[i].y1,e[i].y2,e[i].x1,i);
		change(1,1,m,e[i].y1,e[i].y2,e[i].x2,i);
	}
	for(int i=1;i<=n;i++) ans+=(f[i]==i); write(ans);
}

标签:unlocked,int,void,CSP2024,Tp,read,inline,加赛,NOIP2024
From: https://www.cnblogs.com/Charlieljk/p/18528886

相关文章

  • 多校A层冲刺NOIP2024模拟赛18
    多校A层冲刺NOIP2024模拟赛18\(T1\)A.选彩笔(rgb)\(100pts/100pts\)观察到\(0\ler,g,b\le255\)且答案具有单调性,故考虑二分答案。将\(r,g,b\)分别抽象成三维坐标下的\(x,y,z\)。设当前二分出的答案为\(mid\),由调整法分析可知若存在一个边长为\(mid\)的......
  • CSP2024-S GD 迷惑行为大赏
    CSP2024-SGD迷惑行为大赏CSP2024-SGD迷惑行为大赏:编译错误(版本一)-caijianhong-博客园CSP2024-SGD迷惑行为大赏:编译错误(版本二)-caijianhong-博客园CSP2024-SGD迷惑行为大赏:英文注释-caijianhong-博客园CSP2024-SGD迷惑行为大赏:中文字符-caijianhong-......
  • CSP2024 总结
    CSP2024总结目标第一题要写出来第二题尽量写出来,若写不出来就尽量把能拿的暴力和特殊性质都拿了第三题写出暴力与特殊性质第四题尽量写出暴力预计:180+场上情况刚开始10分钟用指针把T1给写出来了然后去看后面的题刚看到T2感觉不太会,于是打算写特殊性质A,B1.5个小......
  • [61] (多校联训) A层冲刺NOIP2024模拟赛18
    无论从什么意义上都能称得上挂75分的一场A.选彩笔好题答案显然可以二分突然发现我好像专长二分答案钦定最大差值\(dx\),将所有物品以\((r,g,b)\)看成三维空间坐标内的点,原问题可以转化成问空间里一个边长为\(dx\)的立方体内是否有至少\(k\)个点考虑到值域不大,可......
  • CSP2024-S GD 迷惑行为大赏:编译错误(版本二)
    全部信息的,只有部分被去重,观感可能不如这个版本好:https://www.cnblogs.com/caijianhong/p/18526161成功产生18946行、100017词、1368719字节的编译错误,具体多少个过编要再统计一下。无maincomplie:answers/GD-S00045/arena/arena.cpp/usr/bin/ld:/usr/lib/gcc/x86_64-l......
  • [赛记] 多校A层冲刺NOIP2024模拟赛16 && 17
    四舍五入100pts对于一个数$x$,可以发现它的答案可以分成两部分,一部分在$[2x+1,n]$范围内,一部分在小于它的数的范围内,前者$\Theta(1)$算,对于后者,我们发现满足这个要求的数$y$一定有$x\mody<w(x,y)$($w(x,y)$定义为如果$x\mody=0$,则$w(a,......