NOIP2024模拟1
NOIP2024模拟1
\(T1\) GHzoj 3752. 分糖果 \(100pts\)
-
数据加强版: U203804 test1(只输出最多小组数)
-
设最终答案中有 \(a\) 个小组中的小朋友的糖数 \(\bmod 3\) 均等于 \(1\) , \(b\) 个小组中的小朋友的糖数 \(\bmod 3\) 互不相等, \(c\) 个小组中的小朋友的糖数 \(\bmod 3\) 均等于 \(0\) , \(d\) 个小组中的小朋友的糖数 \(\bmod 3\) 均等于 \(2\) 。
-
得到不等式组 \(\begin{cases} 0 \le 3a+b \le cnt_{1} \\ 0 \le 3c+b \le cnt_{0} \\ 0 \le 3d+b \le cnt_{2} \\ 0 \le b \le \min(cnt_{0},cnt_{1},cnt_{2}) \end{cases}\) ,枚举 \(b \in [0,\min(cnt_{0},cnt_{1},cnt_{2})]\) ,有 \(\begin{cases} \max \{ a \}=\left\lfloor \frac{cnt_{1}-b}{3} \right\rfloor \\ \max \{ c \}=\left\lfloor \frac{cnt_{0}-b}{3} \right\rfloor \\ \max \{ d \}=\left\lfloor \frac{cnt_{2}-b}{3} \right\rfloor \end{cases}\) ,计算 \(\max(a+b+c+d)\) 即可。
-
最终,有 \(\max\limits_{b=0}^{\min(cnt_{0},cnt_{1},cnt_{2})} \{ \left\lfloor \frac{cnt_{1}-b}{3} \right\rfloor+b+\left\lfloor \frac{cnt_{0}-b}{3} \right\rfloor+\left\lfloor \frac{cnt_{2}-b}{3} \right\rfloor \}\) 即为所求。
点击查看代码
ll f[100010],cnt[5]; queue<ll>q[5]; int main() { ll n,ans=0,pos=0,i,j,k,a,b,c,d; cin>>n; for(i=1;i<=n;i++) { cin>>f[i]; cnt[f[i]%3]++; q[f[i]%3].push(i); } for(b=0;b<=min(cnt[0],min(cnt[1],cnt[2]));b++) { a=(cnt[1]-b)/3; c=(cnt[0]-b)/3; d=(cnt[2]-b)/3; if(a+b+c+d>ans) { ans=a+b+c+d; pos=b; } } cout<<ans<<endl; if(ans!=0) { for(i=1;i<=pos;i++) { for(k=0;k<=2;k++) { cout<<q[k].front()<<" "; q[k].pop(); } cout<<endl; } for(k=0;k<=2;k++) { for(i=1;i<=(cnt[k]-pos)/3;i++) { for(j=1;j<=3;j++) { cout<<q[k].front()<<" "; q[k].pop(); } cout<<endl; } } } return 0; }
\(T2\) GHzoj 3753. 乒乓球 \(30pts\)
-
部分分
-
\(30pts\) :\(O(n)\) 枚举。
点击查看代码
char s[100010]; int main() { ll n,k,suma=0,sumb=0,ansa=0,ansb=0,i; cin>>n>>k>>(s+1); for(i=1;i<=n;i++) { if(s[(i-1)%k+1]=='A') { suma++; if(suma>=11&&suma-sumb>=2) { ansa++; suma=sumb=0; } } else { sumb++; if(sumb>=11&&sumb-suma>=2) { ansb++; suma=sumb=0; } } } cout<<ansa<<":"<<ansb<<endl; return 0; }
-
-
正解
- 猜测在一定条件下,
zwh
和小红的得分是会有循环节的。而这个条件主要限制来自 \(n\) 和追分环节。 - 预先排除无意义追分,比如
ABAB
或BABA
。 - 若在新的一轮环节开始时,
zwh
和小红已经进入追分环节,二人分数同时减少一个定值使其缩小至 \([0,11]\) ,再接着开始这一轮分数同时减少这一操作对他们的结果(此时不再管分数 \(\ge 11\) 的限制)是没有影响的。 - 设 \(st_{a,b}\) 表示最早是第几个球时
zwh
和小红的单局比赛比分为 \(a:b\) ,此时zwh
和小红分别胜了 \(va_{a,b},vb_{a,b}\) 场。当下一次遇到zwh
和小红的单局比赛比分为 \(a:b\) 时,设此时是第 \(st'\) 个球,zwh
和小红分别胜了 \(A,B\) 场,则可以将其看作 \(st_{a,b} \sim st'\) 是一个周期,zwh
和小红分别增加的胜的场数为 \(A-va_{a,b},B-vb_{a,b}\) 。跳出整个大周期后继续处理剩余的时间即可。
点击查看代码
ll len[100010],to[100010],va[100010],vb[100010]; char s[100010]; void ask(ll pos,ll newlen,ll newa,ll newb,ll &ansa,ll &ansb,ll n) { if(len[pos]<=n) { ansa+=va[pos]+(n-len[pos])/(newlen-len[pos])*(newa-va[pos]);//计算循环节的贡献 ansb+=vb[pos]+(n-len[pos])/(newlen-len[pos])*(newb-vb[pos]); n=(n-len[pos])%(newlen-len[pos]);//除去多出来的部分 } else { pos=0; } ll st=pos; while(len[to[pos]]-len[st]<=n&&to[pos]!=st) { pos=to[pos]; } ansa+=va[pos]-va[st];//计算多出来的贡献 ansb+=vb[pos]-vb[st]; } int main() { ll n,k,suma,sumb,ansa=0,ansb=0,flag=0,i,j,pos; cin>>n>>k>>(s+1); s[0]=s[k]; memset(to,0x3f,sizeof(to)); for(i=0;i<=k;) { pos=i; suma=sumb=0; for(j=1;;j++)//开始找循环节,记录步数 { if(j>=max(k,22ll)*2+2)//无意义追分 { flag=1; break; } i=(i+1)%k; suma+=(s[i]=='A'); sumb+=(s[i]=='B'); if(suma>=11&&suma-sumb>=2) { to[pos]=i; if(to[i]<k)//第二次遇到,找到循环节了 { ask(i,len[pos]+j,va[pos]+1,vb[pos],ansa,ansb,n); flag=1; } len[i]=len[pos]+j; va[i]=va[pos]+1; vb[i]=vb[pos]; break; } if(sumb>=11&&sumb-suma>=2) { to[pos]=i; if(to[i]<k)//第二次遇到,找到循环节了 { ask(i,len[pos]+j,va[pos],vb[pos]+1,ansa,ansb,n); flag=1; } len[i]=len[pos]+j; va[i]=va[pos]; vb[i]=vb[pos]+1; break; } } if(flag==1) { break; } } cout<<ansa<<":"<<ansb<<endl; return 0; }
- 猜测在一定条件下,
\(T3\) GHzoj 3754. 与或 \(15pts\)
-
部分分
-
正解
- 容易有 \(\begin{cases} x|y \ge \max(x,y) \\ x \& y \le \min(x,y) \\ x \& y \le x|y \end{cases}\) ,进而得到 \(x|y \& z \le \min(x|y,z) \le \max(x \& y,z) \le x \& y|z\) ,即若要使表达式值大需要尽可能将 \(\&\) 放到 \(|\) 前面。但要使字典序小需要尽可能将 \(|\) 放到 \(\&\) 前面。故可以得知最后的答案一定形如
|...|&...&|...|
,其中&...&
中可能会夹杂着|
。 - 展开后按位前缀和统计 \(1\) 的个数,接着手动实现 \(\&\) 和 \(|\) ,在不影响理论答案最大时将选择合适的 \(|\) 与 \(\&\) 交换。
点击查看代码
ll a[200010],sum[200010][70]; char s[200010]; ll ask(ll l,ll k,ll n,ll num) { if(l<=n-k)//[l,n-k] 是 & { for(ll i=0;i<=60;i++) { if(((num>>i)&1)&&sum[n-k][i]-sum[l-1][i]!=n-k-l+1) { num-=(1ll<<i); } } } if(n-k+1<=n)//(n-k,n] 是 | { for(ll i=0;i<=60;i++) { if(sum[n][i]-sum[n-k+1-1][i]!=0) { num|=(1ll<<i); } } } return num; } int main() { ll n,k,ans,num,i,j; cin>>n>>k; for(i=1;i<=n;i++) { cin>>a[i]; for(j=0;j<=60;j++) { sum[i][j]=sum[i-1][j]+((a[i]>>j)&1); } } ans=ask(2,k,n,a[1]); cout<<ans<<endl; num=a[1]; for(i=2;i<=n;i++) { if(k>=1&&ask(i+1,k-1,n,num|a[i])==ans) { num|=a[i]; k--; cout<<"|"; } else { num&=a[i]; cout<<"&"; } } return 0; }
- 容易有 \(\begin{cases} x|y \ge \max(x,y) \\ x \& y \le \min(x,y) \\ x \& y \le x|y \end{cases}\) ,进而得到 \(x|y \& z \le \min(x|y,z) \le \max(x \& y,z) \le x \& y|z\) ,即若要使表达式值大需要尽可能将 \(\&\) 放到 \(|\) 前面。但要使字典序小需要尽可能将 \(|\) 放到 \(\&\) 前面。故可以得知最后的答案一定形如
-
\(T4\) GHzoj 3755. 跳舞 \(0pts\)
- 令 \(a_{0}=a_{n+1}=1\) 。
-
设 \(f_{l,r}\) 表示 \([l,r]\) 是否可以全部离开,状态转移方程为 \(f_{l,r}=\max\limits_{k=l}^{r} \{ f_{l,k-1} \times f_{k+1,r} \times [\gcd(a_{k},a_{l-1})>1 \lor \gcd(a_{k},a_{r+1})>1] \}\) ,边界为 \(\begin{cases} f_{l,r}=1 & 0 \le r<l \le n+1 \\ f_{i,i}=[\gcd(a_{i},a_{i-1})>1 \lor \gcd(a_{i},a_{i+1})>1] & i \in [1,n+1] \end{cases}\) 。
-
边界如果处理得不恰当,则会被 \(hack\) 。
点击查看 hack 数据 1
in: 5 3 8 9 20 18 ans: 4
点击查看 hack 数据 2
in: 3 342982640 708917468 453005496 ans: 2
-
-
设 \(dp_{i}\) 表示前 \(i\) 个人中在留下第 \(i\) 个人的情况下最多能离开多少个人。状态转移方程为 \(dp_{i}=\max\limits_{j=0}^{i-1} \{ dp_{j}+f_{j+1,i-1} \times ((i-1)-(j+1)+1) \}\) ,边界为 \(dp_{0}=dp_{1}=0\) 。
-
最终,有 \(dp_{n+1}\) 即为所求。
- 其实 \(dp_{n}\) 也是正确的。因为当最终第 \(n\) 个人和另一个人跳舞后,第 \(n\) 个人离开但另一个人留下时完全可以转化为另一个人离开但第 \(n\) 个人留下,只是涉及钦定让谁留下的问题。
点击查看代码
ll a[510],d[510][510],f[510][510],dp[510]; ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; } int main() { ll n,i,j,k,len,l,r; cin>>n; a[0]=1; for(i=1;i<=n;i++) { cin>>a[i]; } a[n+1]=1; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { d[i][j]=gcd(a[i],a[j]); } } for(i=1;i<=n+1;i++) { for(j=0;j<=i-1;j++) { f[i][j]=1; } if(d[i][i-1]>1||d[i][i+1]>1) { f[i][i]=1; } } for(len=2;len<=n;len++) { for(l=1,r=l+len-1;r<=n;l++,r++) { for(k=l;k<=r;k++) { f[l][r]=max(f[l][r],f[l][k-1]*f[k+1][r]*(d[k][l-1]>1||d[k][r+1]>1)); } } } dp[0]=dp[1]=0; for(i=2;i<=n+1;i++) { for(j=0;j<=i-1;j++) { dp[i]=max(dp[i],dp[j]+f[j+1][i-1]*((i-1)-(j+1)+1)); } } cout<<dp[n+1]<<endl; return 0; }
-
\(T5\) GHzoj 3752. 音乐播放器 \(0pts\)
-
部分分
-
正解
- 设 \(f_{i,j}\) 表示听了 \(i\) 种歌,总愉悦程度为 \(j\) 的方案数,因为有 \(id\) 的限制,填表法有点难写,考虑刷表,有 \(f_{i+1,j+a_{k}}+=f_{i,j} (k \ne id)\) ,边界为 \(f_{0,0}=1\) 。
- 对于每个 \(id \in [1,n]\) 均进行一次 \(DP\) ,最终有 \(\sum\limits_{i=0}^{n-1}\sum\limits_{j=s-a_{id}}^{s-1} \dfrac{f_{i,j} \times A_{i}^{i}}{A_{n}^{i+1}}\) 即为所求。单次询问时间复杂度为 \(O(n^{2}s)\) ,复杂度瓶颈在状态转移过程中的枚举 \(k\) 。
- 枚举 \(k\) 实际上是计算除 \(id\) 以外的贡献,这就提醒我们可以先计算总的贡献,然后将贡献撤销回去从而统计单个答案,再把答案加回来,实质上是一个回退背包。
点击查看代码
const ll p=998244353; ll a[110],inv[110],jc[110],jc_inv[110],f[110][10010]; ll A(ll n,ll m,ll p) { return (n>=m&&n>=0&&m>=0)?jc[n]*jc_inv[n-m]%p:0; } ll A_inv(ll n,ll m,ll p) { return (n>=m&&n>=0&&m>=0)?jc_inv[n]*jc[n-m]%p:0; } int main() { ll n,s,ans=0,i,j,k; cin>>n>>s; for(i=1;i<=n;i++) { cin>>a[i]; } inv[1]=1; jc[0]=jc_inv[0]=jc[1]=jc_inv[1]=1; for(i=2;i<=n;i++) { inv[i]=(p-p/i)*inv[p%i]%p; jc[i]=jc[i-1]*i%p; jc_inv[i]=jc_inv[i-1]*inv[i]%p; } f[0][0]=1; for(i=1;i<=n;i++) { for(j=n-1;j>=0;j--) { for(k=0;k<=s-a[i]-1;k++) { f[j+1][k+a[i]]=(f[j+1][k+a[i]]+f[j][k])%p; } } } for(i=1;i<=n;i++) { ans=0; for(j=0;j<=n-1;j++) { for(k=0;k<=s-a[i]-1;k++) { f[j+1][k+a[i]]=(f[j+1][k+a[i]]-f[j][k]+p)%p; } } for(j=0;j<=n-1;j++) { for(k=s-a[i];k<=s-1;k++) { ans=(ans+(f[j][k]*A(j,j,p)%p)*A_inv(n,j+1,p)%p)%p; } } for(j=n-1;j>=0;j--) { for(k=0;k<=s-a[i]-1;k++) { f[j+1][k+a[i]]=(f[j+1][k+a[i]]+f[j][k])%p; } } cout<<ans<<" "; } return 0; }
-
总结
- 有大样例,赢。
- \(T1\) 喜提除
admin
外最劣解。一开始少了一种情况下发大样例后调了半天才发现,还特意写了个阉割般的Special Judge
来检验答案,力挽狂澜,要不然就没 @STA_Morlin 分高了。 - \(T3\) 因为
1<<i
最终的类型是int
,但1ll<<i
最终的类型是long long
调了半天。- 以前在 挂分记(持续更新ing...) luogu月赛 10.2 基础赛 见过这个,但没想起来。
- \(T4\) 看起来像是有后效性 \(DP\) ,等把状态转移方程写出后发现有 \(\max\) ,高斯消元解不了。
后记
-
信息与公告如下。要是明年 \(J\) 组是这个难度,直接爆炸了。
-
题目背景多次出现 \(huge\) ,\(T4\) 样例解释中出现赵喜娜。
-
\(T1\) 赛时才添加的
Special Judge
,导致交的早的没有Special Judge
,交的晚的才有Special Judge
。赛后安排了重测。 -
数据太水了,放掉了不少的假贪心和边界处理不恰当的代码。
-
官方题解中防抄袭代码明显。
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18290590,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。