2024初三集训模拟测试2
2024初三集训模拟测试2
A. 小P的2048 (100pts)
可能是OJ懒得好好出题了,题目都是图片。
一道大模拟,花些时间慢慢写就能A,我赛时好像调了 \(1h\) 吧。就是注意审题,易错的地方很多
-
合并是不能连续的,两个数字合并之后得到新数字不会在本次操作中继续合并。
-
有效操作是指操作后,(添加新数字之前)棋盘没有任何变化,数字位置变化也视为有效
-
游戏改动后,一旦遇到无效操作,就会结束游戏
其他没什么了。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,a[10][10],sx1,sx2,sy1,sy2,sv1,sv2;
int h[10][10];
int d,v,k,times,ans,r;
deque<int>q;
signed main()
{
scanf("%lld%lld",&n,&m);
scanf("%lld%lld%lld%lld%lld%lld",&sx1,&sy1,&sv1,&sx2,&sy2,&sv2);
a[sx1][sy1]=sv1;
a[sx2][sy2]=sv2;
while(m--){
r=0;
scanf("%lld%lld%lld",&d,&k,&v);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
h[i][j]=a[i][j];
}
}
if(d==0){
for(int j=1;j<=n;j++){
bool flag=0;
for(int i=1;i<=n;i++){
if(a[i][j]!=0){
if(!q.empty()&&a[i][j]==q.back()&&flag==0){
q.pop_back();
q.push_back(a[i][j]*2);
ans+=a[i][j]*2;
flag=1;
a[i][j]=0;
continue;
}
q.push_back(a[i][j]);
a[i][j]=0;
flag=0;
}
}
for(int i=1;i<=n;i++){
if(q.empty())break;
a[i][j]=q.front();
q.pop_front();
}
}
}
else if(d==1){
for(int j=1;j<=n;j++){
bool flag=0;
for(int i=n;i>=1;i--){
if(a[i][j]!=0){
if(!q.empty()&&a[i][j]==q.back()&&flag==0){
q.pop_back();
q.push_back(a[i][j]*2);
ans+=a[i][j]*2;
a[i][j]=0;
flag=1;
continue;
}
q.push_back(a[i][j]);
a[i][j]=0;
flag=0;
}
}
for(int i=n;i>=1;i--){
if(q.empty())break;
a[i][j]=q.front();
q.pop_front();
}
}
}
else if(d==2){
for(int i=1;i<=n;i++){
bool flag=0;
for(int j=1;j<=n;j++){
if(a[i][j]!=0){
if(!q.empty()&&a[i][j]==q.back()&&flag==0){
q.pop_back();
q.push_back(a[i][j]*2);
ans+=a[i][j]*2;
a[i][j]=0;
flag=1;
continue;
}
q.push_back(a[i][j]);
a[i][j]=0;
flag=0;
}
}
for(int j=1;j<=n;j++){
if(q.empty())break;
a[i][j]=q.front();
q.pop_front();
}
}
}
else{
for(int i=1;i<=n;i++){
bool flag=0;
for(int j=n;j>=1;j--){
if(a[i][j]!=0){
if(!q.empty()&&a[i][j]==q.back()&&flag==0){
q.pop_back();
q.push_back(a[i][j]*2);
ans+=a[i][j]*2;
a[i][j]=0;
flag=1;
continue;
}
q.push_back(a[i][j]);
a[i][j]=0;
flag=0;
}
}
for(int j=n;j>=1;j--){
if(q.empty())break;
a[i][j]=q.front();
q.pop_front();
}
}
}
bool work=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(a[i][j]!=h[i][j])work=1;
}
}
if(work==0)break;
times++;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(a[i][j]==0){
r++;
}
}
}
if(r==0)break;
r=(k%r)+1;
int tot=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(a[i][j]==0){
tot++;
if(tot==r)a[i][j]=v;
}
}
}
}
printf("%lld\n%lld",times,ans);
}
B. 子集 (0pts)Special Judge
构造题,将 $ 1,2,\dots ,n $ 划分为 \(k\) 个子集,使每个子集元素个数相同,元素之和相同,保证 $ k\mid n $
思路
令 $ s=\frac{n}{k} $ 即每个子集元素个数
当 \(s\) 为偶数时,构造方法显然,我们只要保证每两个元素加和相同,则构造的数列也一定相同,按照一种蛇形的方法构造即可。
当 \(s\) 为奇数时,相对复杂,我们保证每个子集前两项之和为等差数列,不妨令公差为1,以 \(n=9,k=3\) 为例,得到如下构造:
9 5 1
7 6 2
8 4 3
发现 \(9+5=14,7+6=13,8+4=12\)
当\(k\)为奇数时,我们求得前两项之和的平均数为\(p\),显然最大一项为 \(p+ \left \lfloor \frac{k}{2} \right \rfloor\),由大到小匹配,\(9对应的数字很明显是(14-9=5)\),若我们把 \(8\) 和 \(13\) 对应起来,发现与上一组冲突了,所以可以匹配 \(7\) 和 \(13\),最后匹配 \(8\) 和 \(12\)。
当\(k\)为偶数时,\(s\)是奇数,\(n=s\times k\) 是偶数,
平均数 $$ \frac{n\times (n+1)}{2\times k} $$
无解
Code
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
long long T,n,k,s;
vector<int>v[N];
signed main()
{
scanf("%lld",&T);
while(T--){
scanf("%lld%lld",&n,&k);
if(n==1){
puts("Yes");
puts("1");
continue;
}
if((long long)(n*n+n)%(long long)(2*k)!=0||n==k){
puts("No");
continue;
}
s=n/k;
puts("Yes");
if(s%2==0){
for(int i=1,j=1,l=1;i<=n;i++,j+=l){
v[j].push_back(i);
if(j==k&&l==1){
j=k+1;
l=-1;
}
if(j==1&&l==-1){
j=0;
l=1;
}
}
for(int i=1;i<=k;i++){
while(!v[i].empty()){
printf("%d ",v[i].back());
v[i].pop_back();
}
puts("");
}
}
else{
int w=n-2*k;
long long p=((n*(n+1)/2)-(w*(w+1)/2))/k;
long long t=p+k/2,ord=1;
for(int i=1,j=n;i<=k;i+=2,j-=2,t--){
v[ord].push_back(j);
v[ord].push_back(t-j);
ord++;
}
for(int i=2,j=n-1;i<=k;i+=2,j-=2,t--){
v[ord].push_back(j);
v[ord].push_back(t-j);
ord++;
}
for(int i=1,j=1,l=1;i<=n-2*k;i++,j+=l){
v[j].push_back(i);
if(j==k&&l==1){
j=k+1;
l=-1;
}
if(j==1&&l==-1){
j=0;
l=1;
}
}
for(int i=1;i<=k;i++){
while(!v[i].empty()){
printf("%d ",v[i].back());
v[i].pop_back();
}
puts("");
}
}
}
}
C. 混凝土粉末 (84pts)
赛时做法
在线做法,我们用一个结构体存储每个修改操作,每遇到一个查询操作,遍历在这之前所有修改,将改点的高度累加,输出结果,对于这个做法,似乎人人都要问一句是怎么得\(84pts\)
复杂度分析
令出现 \(p\) 个1操作,\(q-p\) 个2操作,考虑最坏情况,所有查询都在修改后,复杂度 $p\times(q-p) $,显然二次函数,当 $ p=\frac{1}{2}\times q $,取最大值 $\frac{1}{4}\times q^2 $
又因为时间限制 $ 6000ms $ ,\(q\le 10^5\) 都可以跑过。
Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+100;
int n,q,p,num;
ll x,y;
struct node{
int l,r,id;
ll h;
}name[N];
ll height;
int main()
{
scanf("%d%d",&n,&q);
for(int t=1;t<=q;t++){
scanf("%d",&p);
if(p==1){
++num;
scanf("%d%d%lld",&name[num].l,&name[num].r,&name[num].h);
name[num].id=t;
}
else{
scanf("%lld%lld",&x,&y);
height=0;
bool flag=0;
for(int i=1;i<=num;i++){
if(name[i].l<=x&&x<=name[i].r){
height+=name[i].h;
if(height>=y){
flag=1;
printf("%d\n",name[i].id);
break;
}
}
}
if(flag==0)puts("0");
}
}
}
正解
树状数组+二分
我们将每个修改差分,当前坐标的高度就是差分后的前缀和,用树状树组维护。对于查询操作,我们要保证两个限制:
-
只有修改的操作编号在查询前才会对结果有效
-
坐标小于当前节点的前缀和才会有效
对于普通的树状数组2很好维护,但不能保证1,因此有一定改变。
树状数组下标为操作编号,并枚举x坐标,显然能保证限制,并且我们发现随操作增加,当前坐标的高度单调不下降,考虑二分答案。
Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int long long
const int N=1e6+7;
int n,q,t[N],l,r;
ll x,y,h;
struct change{
int id;
ll num;
};
struct query{
int id;
ll yyy;
};
vector<change>g[N];
vector<query>p[N];
ll tree[N],answer[N];
inline int lowbit(int x){
return x&(-x);
}
void Add(int x,int c){
for(int i=x;i<=q;i+=lowbit(i)){
tree[i]+=c;
}
}
ll Query(int x){
ll ans=0;
for(int i=x;i>=1;i-=lowbit(i)){
ans+=tree[i];
}
return ans;
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
scanf("%d%d",&n,&q);
for(int i=1;i<=q;i++){
scanf("%d",&t[i]);
if(t[i]==1){
scanf("%d%d%lld",&l,&r,&h);
g[l].push_back({i,h});
g[r+1].push_back({i,-h});
}
else{
scanf("%lld%lld",&x,&y);
p[x].push_back({i,y});
}
}
for(int i=1;i<=n;i++){
for(auto j:g[i]){
Add(j.id,j.num);
}
for(auto j:p[i]){
if(Query(j.id)<j.yyy){
answer[j.id]=0;
continue;
}
int left=1,right=j.id-1;
while(left<=right){
int mid=(left+right)>>1;
if(Query(mid)>=j.yyy)right=mid-1;
else left=mid+1;
}
answer[j.id]=left;
}
}
for(int i=1;i<=q;i++){
if(t[i]==2){
printf("%lld\n",answer[i]);
}
}
}
D. 排水系统(0pts)
不会,咕了
闲话
四题一共 \(400pts\) ,但是Shadow在赛后取得了 \(500pts\) 的好成绩,因为T1本来出的是原题“谜之阶乘”,Shadow通过贺代码40秒首A,后来题目更改了,Shadow得分没有重置,累加到原分数上。
现在知道谁找原题最快了。