20211102NOIP模拟赛
阿巴巴巴,今天又考了一次模拟赛。
不得不说,自己学校出的题就是香,做的最爽的一次.
但是一点也不妨碍我挂掉了60分(局部变量手残不定义为0)。
无语,要不然就是195分了。哎~~~
第一题:
大水题,难度大概是是普及组吧!没错我还是挂掉了,而且还做了将近两个多小时。
思路:
对于每一个操作都设一个函数。比较难的就是九宫格的访问。于是遍历每一个点,然后预处理出它所在的九宫格,放进一个二维数组维护一下。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
char Map[30][30];
int temp[110][30][30];
int Q;
void print(int cnt){
for(int i=1;i<=9;i++){
printf("+-+-+-+-+-+-+-+-+-+\n");
for(int j=1;j<=9;j++){
printf("|%d",temp[cnt][i][j]);
}
printf("|");
printf("\n");
}
printf("+-+-+-+-+-+-+-+-+-+\n");
return ;
}
int hang[101],lie[101],square[101][101];
int from[101][101],sit[101][101];
int check(int x,int y,int val,int cnt){
for(int i=1;i<=9;i++){
if(temp[cnt][x][i]==val){
return 2;
}
}
for(int i=1;i<=9;i++){
if(temp[cnt][i][y]==val){
return 3;
}
}
int a=from[x][y]/10;
int b=from[x][y]%10;
for(int i=(a-1)*3+1;i<=a*3;i++){
for(int j=(b-1)*3+1;j<=b*3;j++){
if(temp[cnt][i][j]==val){
return 4;
}
}
}
return 0;
}
void Insert(int x,int y,int val,int cnt){
if(temp[cnt][x][y]){
printf("Error!\n");
return ;
}
int rs=check(x,y,val,cnt);
if(rs==2){
printf("Error:row!\n");
return ;
}else if(rs==3){
printf("Error:column!\n");
return ;
}else if(rs==4){
printf("Error:square!\n");
}else if(rs==0){
printf("OK!\n");
temp[cnt][x][y]=val;
}
}
void Delete(int x,int y,int cnt)
{
if(temp[cnt][x][y]==0){
printf("Error!\n");
return ;
}else{
temp[cnt][x][y]=0;
printf("OK!\n");
}
return ;
}
void Query(int x,int y,int cnt){
if(temp[cnt][x][y]!=0){
printf("Error!\n");
return ;
}
int tot=0;//这里我挂了60分
int ans[101];
for(int i=1;i<=9;i++){
if(check(x,y,i,cnt)==0){
ans[++tot]=i;
}
}
printf("%d\n",tot);
for(int i=1;i<=tot;i++){
printf("%d\n",ans[i]);
}
return ;
}
void Merge(int a,int b,int cnt){
int tot1=0,tot2=0;
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
if(temp[a][i][j]!=0){
if(check(i,j,temp[a][i][j],cnt)==0){
temp[cnt][i][j]=temp[a][i][j];
tot1++;
}
}else if(temp[b][i][j]!=0){
if(check(i,j,temp[b][i][j],cnt)==0){
temp[cnt][i][j]=temp[b][i][j];
tot2++;
}
}
}
}
printf("%d %d\n",tot1,tot2);
return ;
}
int main()
{
freopen("sudoku.in","r",stdin);
freopen("sudoku.out","w",stdout);
for(int i=1;i<=19;i++){
cin>>(Map[i]+1);
}
for(int i=1;i<=19;i++){
for(int j=1;j<=19;j++){
if(i%2==0&&j%2==0){
temp[0][(i)/2][(j)/2]=Map[i][j]-'0';
}
}
}
//下面都不用看了,之前写的状态压缩懒得删,唯一有用的就是from
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
hang[i]=hang[i]*10+temp[0][i][j];
lie[j]=lie[j]*10+temp[0][i][j];
}
}
for(int i=1;i<=3;i++){
for(int j=1;j<=9;j++){
if(j<=3){
square[1][1]*=10;
square[1][1]+=temp[0][i][j];
from[i][j]=11;
}else if(j>3&&j<=6){
square[1][2]*=10;
square[1][2]+=temp[0][i][j];
from[i][j]=12;
}else{
if(j>6){
square[1][3]*=10;
square[1][3]+=temp[0][i][j];
from[i][j]=13;
}
}
}
}
for(int i=4;i<=6;i++){
for(int j=1;j<=9;j++){
if(j<=3){
square[2][1]*=10;
square[2][1]+=temp[0][i][j];
from[i][j]=21;
}else if(j>3&&j<=6){
square[2][2]*=10;
square[2][2]+=temp[0][i][j];
from[i][j]=22;
}else{
if(j>6){
square[2][3]*=10;
square[2][3]+=temp[0][i][j];
from[i][j]=23;
}
}
}
}
for(int i=7;i<=9;i++){
for(int j=1;j<=9;j++){
if(j<=3){
square[3][1]*=10;
square[3][1]+=temp[0][i][j];
from[i][j]=31;
}else if(j>3&&j<=6){
square[3][2]*=10;
square[3][2]+=temp[0][i][j];
from[i][j]=32;
}else{
if(j>6){
square[3][3]*=10;
square[3][3]+=temp[0][i][j];
from[i][j]=33;
}
}
}
}
scanf("%d",&Q);
for(int i=1;i<=Q;i++){
for(int k=1;k<=9;k++){
for(int j=1;j<=9;j++){
temp[i][k][j]=temp[i-1][k][j];
}
}
string op;
int a,b,c;
cin>>op;
if(op[0]=='I'){
scanf("%d %d %d",&a,&b,&c);
Insert(a,b,c,i);
}else if(op[0]=='D'){
scanf("%d %d",&a,&b);
Delete(a,b,i);
}else if(op[0]=='Q'){
scanf("%d %d",&a,&b);
Query(a,b,i);
}else if(op[0]=='M'){
scanf("%d %d",&a,&b);
for(int k=1;k<=9;k++){
for(int j=1;j<=9;j++){
temp[i][k][j]=0;
}
}
Merge(a,b,i);
}else if(op[0]=='P'){
print(i);
}
}
return 0;
}
算法一:
暴力,使用一个线段树或者优先队列委会区间最大值。然后贪心模拟每一tick。
期望得分:20~30分
算法二:
考虑最大值最小最小值最大的通法二分答案。
在的范围内二分最大值,然后用一个函数判断当前最大值是否可以在次之类出现。
时间复杂度
期望得分:60分
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define in read()
int n,m,x,s;
int a[100001];
inline int read()
{
static char ch;
int res=0,sign=1;
while((ch=getchar())<'0'||ch>'9'){
if(ch=='-'){
sign=-1;
}
}
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9'){
res=res*10+ch-'0';
}
return res*sign;
}
int ans=0;
inline bool check(int t,int s){
int cnt=0;
for(register int i=1;i<=n;i++){
if(a[i]-s>t) cnt+=(a[i]-s-t-1)/x+1;
if(cnt>s) return 0;
}
return 1;
}
signed main()
{
n=in,m=in,x=in;
for(register int i=1;i<=n;i++){
a[i]=in;
}
std::sort(a+1,a+n+1);
for(register int i=1;i<=m;i++){
s=in;
int l=0,r=1e18,mid;
while(l<r){
mid=(l+r)>>1;
if(check(mid,s)){
ans=mid,r=mid;
}else{
l=mid+1;
}
}
printf("%lld\n",ans);
}
return 0;
}
考虑继续优化算法二。使用主席树维护区间模数,在的时间找到比大数。
我们再来变一下式子:
由于蒟蒻不会LaTeX所以只好上图:
大家将就一下:)
由图得知:
我们的就是向上取整的和,所以变成上图的式子。
考虑先将小麦成熟天数排序,然后再维护整除的后缀和,倒序插入主席树中。
然后再二分答案就好做了,时间复杂度为。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MX=100005;
const ll oo=1231231231231231;
ll read(){
static char ch;
ll res=0,sign=1ll;
while((ch=getchar())<'0'||ch>'9'){
if(ch=='-')sign=-1;
}
res=ch-'0';
while((ch=getchar())>='0'&&ch<='9'){
res=res*10+ch-'0';
}
return res*sign;
}
struct node{
int ls,rs;
ll x;
};
struct SEGT//主席树封装
{
#define mid ((l+r)>>1)
int cnt;
node tre[MX*75];
void upd(int a){
tre[a].x=tre[tre[a].ls].x+tre[tre[a].rs].x;
}
void add(int &a,ll l,ll r,ll p,ll x){
tre[++cnt]=tre[a];//新建节点
a=cnt;
if(l==r) tre[a].x+=x;
else if(p<=mid) add(tre[a].ls,l,mid,p,x),upd(a);
else add(tre[a].rs,mid+1,r,p,x),upd(a);
}
ll qur(int a,ll l,ll r,ll ql,ll qr){//询问
if(!a) return 0;
else if(l>=ql&&r<=qr){
return tre[a].x;
}else if(qr<l||ql>r){
return 0;
}else {
return qur(tre[a].ls,l,mid,ql,qr)+qur(tre[a].rs,mid+1,r,ql,qr);
}
}
#undef mid
}T;
int n,m;
ll mns;
ll val[MX],id_block[MX],sum_id_block[MX];
int rot[MX];
void init()
{
for(int i=1;i<=n;i++){
id_block[i]=val[i]/mns;//用x的次数
}
for(int i=n;i>=1;i--){
sum_id_block[i]=sum_id_block[i+1]+id_block[i];//计算后缀
rot[i]=rot[i+1];//倒着转入主席树中 ,记录比val[i]大的数中的模数量
T.add(rot[i],0,mns,val[i]%mns,1);//一颗区间树 ,装区间里面的模数
}
}
void input()
{
n=read(),m=read(),mns=read();
for(int i=1;i<=n;i++){
val[i]=read();
}
sort(val+1,val+n+1);//顺便排序
}
ll check(ll tar){
ll ret=0;
int p=lower_bound(val+1,val+n+1,tar)-val;//nlogn找到大等于他的最小数
ret=sum_id_block[p]-(tar/mns)*(n-p+1);//ti/x+v/x 的和
ret+=T.qur(rot[p],0,mns,tar%mns+1,mns);// 比他模数大的数
return ret;
}
ll work(ll t){
ll l=0,r=val[n],ans=0,mid;
while(l<r)
{
mid=(l+r)>>1;
if(check(mid)>t) l=ans=mid+1;
else r=ans=mid;
}
return max(0ll,ans-t);
}
void work_big()
{
for(int i=1;i<=m;i++){
ll t;
t=read();
printf("%lld\n",work(t));
}
}
int main(){
input();//输入
init();//装入主席树中
work_big();
return 0;
}
第三题:
算法一:用预处理出每一个点对的距离,然后查询相加。复杂度
期望得分:40分
算法二:考虑将每一个点作为根节点,遍历整棵树,求出点对距离。
期望得分: 40分
算法三:点分治(好吧逼装多了好像我会正解一样)
第四题:期望毒瘤题(然鹅洛谷上是蓝题??)
算法一:机智如我之枚举,35分到手。
算法二:手摸样例打表,发现时当 当。
50分到手.
算法三:还是看大佬的见解吧:
https://www.luogu.com.cn/blog/wlj55/solution-p3898
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】