省选模拟 2

news/2024/12/25 12:16:16/文章来源:https://www.cnblogs.com/Ishar-zdl/p/18630113

T 营模拟,所以四道题,梦回 NOIP 前模拟赛。

A 石子游戏(stone)

点击查看 天天放博弈论,博弈论是你爹吗?

先打表,发现 B 赢麻了,A 赢的时候几乎全是 \(1\),前面全是 \(1\),后面带了一个 \(2\) 的时候 A 也会赢,由于 A 每次拿一堆,B 每次拿两堆,直接猜答案与 \(1\)\(2\),和其他数的个数有关。
考虑用 DP 打表,\(f_{i,j,k,0/1}\) 表示有 \(i\)\(1\)\(j\)\(2\)\(k\) 个其他,发现可以自己转移自己,但是我们已经钦定了 答案与 1,2,和其他数的个数有关,所以其他数不能变成其他数,只能变成 \(1,2\),所以直接不管,写完后发现这个是对的,并且能过题。
那如果 \(n\) 很大呢,再次观察题意和打的表,首先有一个很简单的情况,如果全是 \(1\),那么当 \(n\equiv 0\pmod 3\) 时,Alice 必败,考虑在全是 \(1\) 的基础上再加一个不为 \(1\) 的数 \(x\),Alice 可以选择拿走 \(x\) 或者把 \(x\) 变成 \(1\),两种总有一种会赢,得出另一个结论,只有一个不为 \(1\) 的数时,Alice 先手必胜。
考虑现在有 \(k\) 个数大于 \(1\)
考虑 Bob 的策略,他一定要尽量避免 \(k=1\)
如果 \(k=2\),那么 Bob 可以增加 \(0,1,2\)\(1\),必胜。如果 \(k=1\),如果这个数大于 \(2\),那么 Bob 可以增加 \(0,1,-1\)\(1\),必胜,如果这个数是 \(2\),那 Bob 只能增加 \(0,-1\)\(1\),如果同时 \(n-1\equiv 2\pmod 3\),那么 Bob 必败,所以这种情况要求一堆 \(1\),一个 \(2\),一个 \(x\)\(n-2\not\equiv 0\pmod 3\) Alice 才能必胜,他可以增加 \(0,1\)\(1\)
其他情况 Bob 都可以及时调整。

点击查看代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii std::pair<int,int>
#define eb emplace_back
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
std::mt19937_64 myrand(std::chrono::high_resolution_clock::now().time_since_epoch().count());
inline int R(int n){return myrand()%n+1;}
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
int n,b[105];
signed main(){// freopen("in.in","r",stdin);freopen("out.out","w",stdout);std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);int T=read();while(T--){int a=0;n=read();for(int i=1;i<=n;++i)b[i]=read(),a+=b[i]==1;if(a==n){if(n%3)std::cout<<"Win\n";else std::cout<<"Lose\n";continue;}if(a==n-1){std::cout<<"Win\n";continue;}if(a==n-2){bool pd=0;for(int i=1;i<=n;++i)if(b[i]==2)pd=1;if(pd&&n%3!=2)std::cout<<"Win\n";else std::cout<<"Lose\n";continue;}std::cout<<"Lose\n";}
}

B 树上字符串(treestr)

不难想到预处理 \(i,j\) 区间在树上的信息,这个复杂度肯定是 \(\mathcal{O}(n|S|^2)\) 了,可以想到倍增或者树剖来拼接信息,但是这样会多一个 \(\log\),考虑还是用树上前缀和来做这个,\(f_{i,j,k}\) 表示从根到 \(i\)\(S[j,k]\) 出现的次数,发现拼接需要容斥,类似于一个退背包的东西,直接从小到大退就行了。

点击查看代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii std::pair<int,int>
#define eb emplace_back
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
std::mt19937_64 myrand(std::chrono::high_resolution_clock::now().time_since_epoch().count());
inline int R(int n){return myrand()%n+1;}
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
const int N=1e5+10,mod=998244353;
inline void W(int &x,int y){x=(x+y)%mod;}
int n,q,d[N][32][32],st[20][N],len,fu[N],tmp1[N],tmp2[N],dfn[N],dn;
char S[N],T[N];
std::vector<int> e[N],v[N];
inline int get(int x,int y){return dfn[x]<dfn[y]?x:y;}
inline void dfs(int x,int fa){fu[x]=fa;for(int i=1;i<=len;++i)for(int j=1;j<=len;++j)d[x][i][j]=d[fa][i][j];for(int y:v[x]){W(d[x][y][y],1);for(int i=y+1;d[fa][i][y+1];++i)W(d[x][i][y],d[fa][i][y+1]);for(int i=y-1;d[fa][i][y-1];--i)W(d[x][i][y],d[fa][i][y-1]);}st[0][dfn[x]=++dn]=fa;for(int v:e[x])if(v^fa)dfs(v,x);if(x==1)for(int i=1;i<=std::__lg(n);++i)for(int j=1;j+(1<<i)-1<=n;++j)st[i][j]=get(st[i-1][j],st[i-1][j+(1<<i-1)]);
}
inline int LCA(int u,int v){if(u==v)return u;if((u=dfn[u])>(v=dfn[v]))std::swap(u,v);int d=std::__lg(v-u++);return get(st[d][u],st[d][v-(1<<d)+1]);
}
inline int query(int u,int v){int lca=LCA(u,v),f=fu[lca],res=0;for(int i=1;i<=len;++i)tmp1[i]=d[u][i][1],tmp2[i]=d[v][i][len];tmp1[0]=1;tmp2[len+1]=1;for(int i=1;i<=len;++i)for(int j=i;j;--j)W(tmp1[i],-1ll*tmp1[j-1]*d[f][i][j]%mod);f=lca;for(int i=len;i;--i)for(int j=i;j<=len;++j)W(tmp2[i],-1ll*tmp2[j+1]*d[f][i][j]%mod);for(int i=0;i<=len;++i)W(res,1ll*tmp1[i]*tmp2[i+1]%mod);return (res+mod)%mod;
}
signed main(){// freopen("in.in","r",stdin);freopen("out.out","w",stdout);std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);std::cin>>n>>q;for(int i=1;i<n;++i){int u,v;std::cin>>u>>v;e[u].eb(v);e[v].eb(u);}std::cin>>T+1>>S+1;len=strlen(S+1);for(int i=1;i<=n;++i)for(int j=1;j<=len;++j)if(T[i]==S[j])v[i].eb(j);dfs(1,0);for(int i=1;i<=q;++i){int u,v;std::cin>>u>>v;std::cout<<query(u,v)<<"\n";}
}

C 区间划分(divid)

注意到这个好区间的数量并不多,最多只有 \(n\log n\) 个,如果能找到所有好区间,就能直接用这些区间来 DP 了,如果一个区间的长度为 \(len\),其中最大的一个数为 \(2^a\),那么 \(x\in[a,a+\log len]\),所以可以直接暴力分治来找到所有区间,每次找到区间中最大值的位置 \(mid\),然后以小的区间中的位置为端点来找另一个区间的端点,用哈希存前缀和即可,需要一个巨大无比的模数。

点击查看代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii std::pair<int,int>
#define eb emplace_back
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
std::mt19937_64 myrand(std::chrono::high_resolution_clock::now().time_since_epoch().count());
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
const int N=6e5+10,mod=1e9+7,M=1e6+30;
const ll P=100000000000000003;
inline void W(int &x,int y){x=(x+y)%mod;}
int n,a[N],st[20][N],f[N],lg[N];
ll p[M],s[N];
std::unordered_map<ll,int> mp;
inline int get(int x,int y){return a[x]<a[y]?y:x;}
inline int Get(int l,int r){int d=lg[r-l+1];return get(st[d][l],st[d][r-(1<<d)+1]);}
inline void sol(int l,int r){if(l>r)return ;if(l==r)return W(f[l],f[l-1]),void();int mid=Get(l,r);sol(l,mid-1);int L=a[mid],R=L+lg[r-l+1];if(mid-l+1<=r-mid){for(int i=l;i<=mid;++i){for(int ai=L;ai<=R;++ai){ll ne=(s[i-1]+p[ai])%P;if(mp.find(ne)!=mp.end()){int wc=mp[ne];if(wc>=mid&&wc<=r)W(f[wc],f[i-1]);}}}}else{for(int i=mid;i<=r;++i){for(int ai=L;ai<=R;++ai){ll ne=(s[i]-p[ai]+P)%P;if(mp.find(ne)!=mp.end()){int wc=mp[ne];if(wc>=l-1&&wc<mid)W(f[i],f[wc]);}}}}sol(mid+1,r);
}
signed main(){// freopen("in.in","r",stdin);freopen("out.out","w",stdout);std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);n=read();f[0]=1;lg[0]=-1;for(int i=1;i<=n;++i)lg[i]=lg[i>>1]+1;p[0]=1;for(int i=1;i<=M-10;++i)p[i]=p[i-1]*2%P;mp[0]=0;for(int i=1;i<=n;++i)a[i]=read(),s[i]=(s[i-1]+p[a[i]])%P,st[0][i]=i,mp[s[i]]=i;for(int i=1;i<=std::__lg(n);++i)for(int j=1;j+(1<<i)-1<=n;++j)st[i][j]=get(st[i-1][j],st[i-1][j+(1<<i-1)]);sol(1,n);std::cout<<f[n]<<'\n';
}

D 机器(machine)

经典水龙头接水问题,一个人的贡献是多少个人(包括自己)在等他,那么最小代价的方案就是前 \(K\) 大有一个人等,第二 \(K\) 大有两个人等,以此类推。
题目没要求在线,可以先把所有人都拿出来,每次操作就相当于停用一个人,启用一个人。把所有人从大到小排序后放到一个 \(m\) 行乘 \(K\) 列的矩形里,第 \(i\) 行的系数是 \(i\),假设两个人的位置分别是 \(l,r(l<r)\),那么他们之间的人都会往前平移一个,只有每行第一个人的系数才会变,\((l>r)\) 同理。
如果 \(m\) 比较小,可以每行维护,如果 \(K\) 比较小,可以每列维护,对 \(K\) 根号分治后做到 \(\mathcal{O}(n\sqrt n\log n)\),过不了。
考虑分块,\(d_{i,j}\) 表示第 \(i\) 个块中模 \(K\)\(j\) 的值的和,注意到每个块中只有 \(B\) 个有效位置,所以可以做一个偏移的东西,再记一个 \(s_i\) 表示第 \(i\) 个块中有效值的数量即可。

点击查看代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define pii std::pair<int,int>
#define eb emplace_back
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
std::mt19937_64 myrand(std::chrono::high_resolution_clock::now().time_since_epoch().count());
inline int R(int n){return myrand()%n+1;}
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
inline void write(__int128 x){if(x>=10)write(x/10);putchar(x%10+'0');}
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
const int N=6e5+10,LEN=800;
int n,K,A[N],Q,tot,num[LEN],match[N],len,ID[N];
ll s[LEN][LEN];
__int128 ans;
bool vis[N];
struct NODE{int x,id;}a[N],b[N];
struct QU{int l,r;}q[N];
inline ll c(int rk,int pos){return 1ll*((rk-1)/K+1)*a[pos].x;}
inline void pro(int id){int l=(id-1)*len+1,r=std::min(tot,l+len-1);for(int i=0;i<=len;++i)s[id][i]=0;num[id]=0;for(int i=l,now=0;i<=r;++i)if(vis[i])s[id][now++%K]+=a[i].x,num[id]++;
}
inline ll get(int id,int x,int pre){int zc=((x-pre)%K+K)%K;if(zc>=len)return 0;return s[id][zc];}
inline void work(int l,int r){int lid=ID[l],rid=ID[r];if(l<r){int rk=0;for(int i=1;i<lid;++i)rk+=num[i];for(int i=l;ID[i]==lid;--i)rk+=vis[i];ans-=c(rk,l);vis[l]=0,vis[r]=1;if(lid==rid){for(int i=l+1;i<r;++i){rk+=vis[i];if(vis[i])ans-=c(rk,i)-c(rk-1,i);}ans+=c(rk,r);pro(lid);}else{for(int i=l+1;ID[i]==lid;++i){rk+=vis[i];if(vis[i])ans-=c(rk,i)-c(rk-1,i);}for(int i=lid+1;i<rid;++i)ans-=get(i,0,rk),rk+=num[i];for(int i=(rid-1)*len+1;i<r;++i){rk+=vis[i];if(vis[i])ans-=c(rk,i)-c(rk-1,i);}ans+=c(rk,r);pro(lid);pro(rid);}}else{std::swap(l,r);std::swap(lid,rid);int rk=0;for(int i=1;i<lid;++i)rk+=num[i];vis[l]=1;vis[r]=0;for(int i=l;ID[i]==lid;--i)rk+=vis[i];ans+=c(rk,l);if(lid==rid){for(int i=l+1;i<r;++i){rk+=vis[i];if(vis[i])ans+=c(rk,i)-c(rk-1,i);}ans-=c(rk,r);pro(lid);}else{for(int i=l+1;ID[i]==lid;++i){rk+=vis[i];if(vis[i])ans+=c(rk,i)-c(rk-1,i);}for(int i=lid+1;i<rid;++i)ans+=get(i,K-1,rk-1),rk+=num[i];for(int i=(rid-1)*len+1;i<r;++i){rk+=vis[i];if(vis[i])ans+=c(rk,i)-c(rk-1,i);}ans-=c(rk,r);pro(lid),pro(rid);}}write(ans);putchar('\n');
}
signed main(){// freopen("in.in","r",stdin);freopen("out.out","w",stdout);std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);n=read(),K=read();for(int i=1;i<=n;++i)A[i]=read(),a[++tot]={A[i],i},b[i]={A[i],i};int Q=read();for(int i=1;i<=Q;++i){int p=read(),x=read();q[i]={b[p].id,n+i};b[p]={x,n+i};a[++tot]={x,n+i};}std::sort(a+1,a+tot+1,[](NODE A,NODE B){return A.x>B.x;});len=std::sqrt(tot);std::sort(A+1,A+n+1,[](int a,int b){return a>b;});for(int i=1;i<=n;++i)ans+=1ll*((i-1)/K+1)*A[i];for(int i=1;i<=tot;++i)match[a[i].id]=i,ID[i]=(i-1)/len+1;for(int i=1;i<=n;++i)vis[match[i]]=1;for(int i=1;i<=Q;++i)q[i].l=match[q[i].l],q[i].r=match[q[i].r];for(int i=1;i<=tot;i+=len)pro(ID[i]);for(int i=1;i<=Q;++i)work(q[i].l,q[i].r);
}

总结

T2 不会大傻逼,赛时感觉容斥不了就把正解弃了。T1 博弈论不会大傻逼。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/858596.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

杭州数据恢复之某公司经理的三星移动硬盘摔坏了盘片划伤二次开盘

这是一块老款三星Samsung使用mini USB接口的500G移动硬盘,采用了一体式电路板,型号是HM502JX。硬盘是用户不小心摔坏了,接电脑不识别而且有异响,先送修到百脑汇电脑城某家数据恢复中心进行开盘修复,但被告知盘片有划伤无法恢复数据。很巧用户公司里的一名员工曾经在我们这…

第十七次作业

1、安装最新版phpstudy集成工具并创建一个网站,编写php代码输出网站信息(phpinfo)2、安装vscode,并安装php开发插件、汉化插件、xdebug等插件 中⽂语⾔包安装php调试插件配置Open PHP/HTML/JS In Browser插件3、配置phpstudy集成工具xdebug扩展,并使用vscode对php代码进行…

dataezse接入zabbix监控

常用查询 目录常用查询zabbix 常用库表说明主机资源监控主机资源监控(纯值)oracle状态监控CPU top10DISK TOP 10Memory TOP 10SPACE USERD TOP 10问题告警级别分布问题列表null问题主机组正常主机总数主机问题排行 zabbix 常用库表说明 https://www.cnblogs.com/yaoyaojcy/p/…

MySQL 千万 级数据量根据(索引)优化 查询 速度

MySQL 千万 级数据量根据(索引)优化 查询 速度| Id | Title | DateAdded | SourceUrl | PostType | Body | BlogId | Description | DateUpdated | IsMarkdown | EntryName | CreatedTime | IsActive | AutoDesc | AccessPermission | | -------------| -------------| -----…

kafka中文教程

kafka中文教程| Id | Title | DateAdded | SourceUrl | PostType | Body | BlogId | Description | DateUpdated | IsMarkdown | EntryName | CreatedTime | IsActive | AutoDesc | AccessPermission | | -------------| -------------| -------------| -------------| -------…

银河麒麟桌面操作系统 使用root登录桌面端

麒麟 V10 桌面操作系统使用 root 登录 - 乔京飞 - 博客园 一、允许使用 root 用户登录 麒麟 V10 桌面操作系统安装过程后(或者安装完成后),必须创建一个新用户才能使用。很多目录中的文件,只能看不能改,甚至连创建一个新文件都不行。在终端执行命令,需要频繁的使用 sudo …

oracle exp 无法导出空表

oracle exp 无法导出空表| Id | Title | DateAdded | SourceUrl | PostType | Body | BlogId | Description | DateUpdated | IsMarkdown | EntryName | CreatedTime | IsActive | AutoDesc | AccessPermission | | -------------| -------------| -------------| ------------…

好奇!J 人电商团队圣诞购物潮,哪 6 款办公软件是效率提升的秘密武器?

随着圣诞节的脚步日益临近,电商零售行业迎来了一年一度的业务高峰。对于 J 人特质鲜明的电商团队而言,高效的工作流程和团队协作机制是应对这一繁忙时期的关键所在。在众多办公软件中,可视化团队协作工具以其直观、高效的特点脱颖而出,成为提升工作效率和个人学习效率的得力…

车企售后服务中的项目管理创新:提升跨部门协作

一、引言 随着数字化技术的飞速发展,越来越多的车企开始向电商平台转型,不仅在销售端实现线上化,也将目光投向了售后服务的优化。传统的汽车售后服务模式主要依赖线下4S店,通过面对面的沟通和维修,服务流程相对固定,且服务质量参差不齐。随着消费者购车行为的逐渐转向线上…

Shiro550漏洞(CVE-2016-4437)

介绍 Apache Shiro 是一个强大易用的 Java 安全框架,提供了认证、授权、加密和会话管理等功能。Shiro 框架直观、易用,同时也能提供健壮的安全性。 漏洞影响版本 Shiro <= 1.2.4 环境搭建 jdk:1.8.0_372 Tomcat8 这里我用的是 p 神的环境 https://github.com/phith0n/Jav…

Kubernetes应用编排控制器

1. Kubernetes控制器模式 1.1 声明式API API设计方法命令式API也称为指令式API,用户需要一步步地告诉机器该如何做(How),机器自身不具有任何“智能”,只被动接受指令 高度依赖用户自身理解和达成目标的能力和处理各类异常问题的经验,实现的是“命令式编程(Imperative Pr…