2024暑假集训测试10
前言
- 比赛链接。
这回挂的比较少,就 T2 唐了太长时间导致 T4 暴力没打完挂了 \(60\),不过 T4 暴力给的非常多,但也并不好打,T3 赛后因数据水安排重测,重测后还往上涨了 \(2\) 名,因为我的解法就是本着 \(60pts\) 部分分去的,没有卡掉我。
T1 花间叔祖
- 原题。
考虑另 \(P=2\) 则 \(ans\) 最大为 \(2\),故只有 \(ans=1\) 时才更优,即所有数 \(\bmod P\) 均同余。
将数组排序,求每相邻两个 \(a_i\) 的差的 \(\gcd\),若最终结果 \(\ne 1\) 则 \(ans=1\)。
T2 合并 r
-
原题。
-
部分分 \(10pts\):爆搜即可。
-
正解:
挺巧妙的一个 DP,感觉赛时不打表大概率想不出来。
定义 \(f_{i,j}\) 表示处理到第 \(i\) 位,当前和为 \(j\) 的方案数,发现若 \(j\) 能被拼出来,则 \(\dfrac{j}{2}\) 也一定能被拼出来,直接全部 \(÷2\) 即可;同样的 \(j+1\) 也一定能被拼出来。
如此只枚举整数的 \(j\),按上述方式转移,即可不重不漏统计答案,有:
\[f_{i,j}=f_{i-1,j-1}+f_{i,j\times 2} \]第一反应会对正确性提出疑问,但是首先因为和为整数,故最后结果为小数的没有任何贡献;\(+1\) 和 \(÷2\) 的前后顺序出来的方案也是不同的,满足了不漏。发现后 \(÷2\) 的一定比先 \(÷2\) 的大,也满足了从小到大排序,做到了不重。
初始值 \(f_{1,1}=1\),答案为 \(f_{n,k}\)。
T3 回收波特
-
原题。
-
部分分,理论 \(30pts\):对于特殊性质 \(1\) 直接 \(O(nm)\) 跑,但好像数据水还能多拿点?
-
部分分,理论 \(60pts\):对于特殊性质 \(2\),值域很小,存在大量重合的点,所有重合的点只处理一个即可。
-
正解:
很巧妙的一个做法,发现对于 \(a_i=-a_j\),即两点关于原点对称,则后面的操作其都对称,只处理一个即可。
随后利用移动原点的方法,发现之前原点两边都有不能移动原点,但现在可以了;对于全在 \(tag\) 右边的 \(tag\) 右移 \(d\),反之左移 \(d\),若仍全在其一边无需操作,否则将小的一边合并到大的一边,直接连边即可。
发现这样的话点的位置从来没动过,所以若无法回收的话其最后的位置就是 \(i-dag\),让后跑 \(dfs\) 将对称的那些处理掉就可以了。
因为值域上每个点最多被处理一次,复杂度 \(O(n+m)\)。
点击查看代码
#include<bits/stdc++.h> #define ll long long #define endl '\n' #define sort stable_sort using namespace std; const int N=1e6+10; const double eps=1e-8; 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 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);} int n,m,l=N-10,r,mid,maxx,tag,stop[N],pos[N],a[N]; bool vis[N]; vector<int>e[N]; void dfs(int x) { if(vis[x]) return ; vis[x]=1; for(int y:e[x]) { if(stop[x]) stop[y]=stop[x]; else pos[y]=-pos[x]; dfs(y); } } signed main() { read(n),read(m); for(int i=1;i<=n;i++) { read(a[i]); l=min(l,a[i]); r=max(r,a[i]); maxx=r; } for(int i=1,d;i<=m;i++) { read(d); if(l+tag>0) tag-=d; else tag+=d; mid=-tag; if(l<=mid&&r>=mid) { stop[mid]=i; if(mid-l<=r-mid) { for(int j=l;j<=mid-1;j++) e[mid*2-j].push_back(j); l=mid+1; } else { for(int j=mid+1;j<=r;j++) e[mid*2-j].push_back(j); r=mid-1; } } } for(int i=l;i<=r;i++) { if(!stop[i]) pos[i]=i+tag; dfs(i); } for(int i=1;i<=l-1;i++) if(stop[i]) dfs(i); for(int i=r+1;i<=maxx;i++) if(stop[i]) dfs(i); for(int i=1;i<=n;i++) { if(stop[a[i]]) printf("Yes %d\n",stop[a[i]]); else printf("No %d\n",pos[a[i]]); } }
T4 斗篷
-
部分分 \(65pts\):
对于每个
x
以他为右下角的点,其向左最多扩展 \(l\),向上最多扩展 \(u\),那么对于每个其向左能扩展到的点向下最多扩展 \(d\),若 \(d>\min(l,u)\) 则产生贡献。暴力预处理然后枚举,复杂度是 \(O(n^3)\) 的。 -
正解:
考虑怎么优化,依然是处理出每个点的 \(l,u,d\),类似于二维偏序的,每个 \(x\) 都能对 \(x+1\sim x+d_x\) 产生贡献,故处理两个二维偏序分别表示询问和修改处理即可,将代码打出来就会感觉挺好理解的。
点击查看代码
#include<bits/stdc++.h> #define ll long long #define endl '\n' #define sort stable_sort using namespace std; const int N=1.2e4+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 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);} int n,m,cs,qs; ll ans; short f[3][N>>1][N]; char s[N>>1][N]; struct bb { ll c[N]; int lowbit(int x) {return x&-x;} void change(int x) {for(;x<=cs;x+=lowbit(x)) c[x]++;} int ask(int l,int r) { l--; ll ans=0; for(;r;r-=lowbit(r)) ans+=c[r]; for(;l;l-=lowbit(l)) ans-=c[l]; return ans; } void clear() {memset(c,0,cs*sizeof(ll));} }t; struct aa {int l,r,val;}q[N],c[N]; bool cmp(aa a,aa b) {return a.val<b.val;} void calc(int s,short *l,short *u,short *d) { cs=0,qs=0; for(int i=s;i<=m;i+=4) { cs++; if(i!=s&&l[i]&&u[i]) q[++qs]={cs-min(l[i],u[i]),cs-1,cs}; c[cs]={cs,cs,d[i]+cs}; } sort(c+1,c+1+cs,cmp),sort(q+1,q+1+qs,cmp); t.clear(); int j=cs; for(int i=qs;i>=1;i--) { for(;j>=1&&c[j].val>=q[i].val;j--) t.change(c[j].l); ans+=t.ask(q[i].l,q[i].r); } } signed main() { read(n),read(m); n=(n<<1)-1; m=(m<<1)-1; char ch=getchar(); for(int i=1;i<=n;i++) { for(;ch=='\n'||ch=='\r';ch=getchar()); for(int j=1;j<=m&&ch!='\n'&&ch!='\r';j++,ch=getchar()) s[i][j]=ch; } for(int i=1;i<=n;i+=2) for(int j=i%4;j<=m;j+=4) { if(j>=4&&s[i][j-1]=='-') f[0][i][j]=f[0][i][j-4]+1; if(i>1&&j>1&&s[i-1][j-1]=='\\') f[1][i][j]=f[1][i-2][j-2]+1; if(i>1&&j<m&&s[i-1][j+1]=='/') f[2][i][j]=f[2][i-2][j+2]+1; } for(int i=3;i<=n;i+=2) calc(i%4,f[0][i],f[1][i],f[2][i]); memset(f[1],0,sizeof(f[1])); memset(f[2],0,sizeof(f[2])); for(int i=n-2;i>=1;i-=2) for(int j=i%4;j<=m;j+=4) { if(j>1&&s[i+1][j-1]=='/') f[1][i][j]=f[1][i+2][j-2]+1; if(j<m&&s[i+1][j+1]=='\\') f[2][i][j]=f[2][i+2][j+2]+1; } for(int i=1;i<=n-1;i+=2) calc(i%4,f[0][i],f[1][i],f[2][i]); write(ans); }
总结
T4 输入比较抽象,fgets OJ 过不去 luogu 能过,一个一个 getchar OJ 能过 luogu 过不去,比较抽象。
T4 挺麻烦的其实,但最近学长和教练好像有意鼓励我们坚持调麻烦的题与暴力,可见这次暴力给的非常多,\(rank1\) 更是一道题没 A,学长说 T4 现在不调以后早晚还会碰到类似解法,所以唐了将近一上午也是给调了。
讲题的时候学长教了对拍,于是学了一下。