noip模拟17

news/2024/11/19 19:44:52/文章来源:https://www.cnblogs.com/ccjjxx/p/18555366

A 选取字符串

\(n^2\)

直接枚举全部的 \(q,p\) 然后开一个二维的 bitset 去存一个数是否是某个数的前后缀。

选到两个 \(p,q\) 时把这两个数的 bitset 与起来,贡献是 \(\binom{count}{k}\)

正解就是先用 kmp 去求出来全部的 border,然后用 border 关系建一棵树,这棵树上满足父亲是儿子的 border。根节点为空串。

然后就是树上选取 \(k\) 个点,求 lca 深度平方和。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10,mod=998244353;
int f[N],g[N];
int ppow(int a,int b)
{int res=1;while(b){if(b&1) res=(res*a)%mod;a=(a*a)%mod,b>>=1;}return res; 
}
int n,k;
void init()
{f[0]=g[0]=1; for(int i=1;i<=n+2;i++) f[i]=(f[i-1]*i)%mod;g[n+1]=ppow(f[n+1],mod-2);for(int i=n;i>=1;i--) g[i]=(g[i+1]*(i+1))%mod;
}
char c[N];
int nxt[N],dep[N],sum[N],cnt[N];
inline int getc(int n,int m)
{if(n<m) return 0;return f[n]*g[m]%mod*g[n-m]%mod;
}
signed main()
{
//	freopen("a2.in","r",stdin);freopen("string.in","r",stdin);freopen("string.out","w",stdout);ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>k>>(c+1);n=strlen(c+1);init();
//	cout<<n<<"\n";for(int i=2,j=0;i<=n;i++){for(;j>0&&c[i]!=c[j+1];j=nxt[j]);if(c[i]==c[j+1])j++;nxt[i]=j;}for(int i=1;i<=n;i++)dep[i]=dep[nxt[i]]+1;for(int i=0;i<=n;i++)sum[i]++;for(int i=n;i>=1;i--)sum[nxt[i]]=sum[nxt[i]]+sum[i];for(int i=0;i<=n;i++)cnt[dep[i]]=(cnt[dep[i]]+getc(sum[i],k))%mod;int ans=0;for(int i=0;i<=n;i++)ans=(ans+1ll*cnt[i]*(2*i+1)%mod)%mod;printf("%d",ans);return 0;
}

B 取石子

其实没看懂题解。然后就粘过来了。。。

问题模型:取石子(NIM)游戏,要求每个人每次取的石子数不能超过上一个人刚刚取的,第一个人最
开始可以取不超过 \(K\) 个。
考虑策略:

  1. 如果 \(\sum_i a_i\) 是奇数,先手取 \(1\) 个必胜,因为每个人之后都只能取 \(1\) 个;

  2. 否则,先手最优一定取偶数个并且后面每个人能取偶数个都只会取偶数个(否则留给对手总和为奇
    数的情况,自己必败),所以可以递归到 \(K\gets \lfloor \frac{K}{2} \rfloor , a_i\gets \lfloor \frac{a_i}{2} \rfloor\)

解得先手必胜当且仅当对于某个 \(t\le \log_2 K\)\(\sum{i=1}^n \lfloor \frac{a_i}{2^t}\rfloor\)\(2\)
\(1\),也即 \(\mathop{\oplus}i a_i \not\equiv 0\pmod {2^{\lfloor \log_2 K\rfloor}}\)

定义 \(\mathrm{lowbit}(x)\) 表示整除 \(x\) 的最大的 \(2\) 的幂。先手第一步能必胜的策略必定是取
\((2k+1)\cdot \mathrm{lowbit}(\mathop{\oplus}i a_i)\) 个。枚举取的堆,假设是第 \(i\) 堆,考虑枚举取
完之后另一个人面对的剩下的异或和的 \(\mathrm{lowbit}\),假设是 \(2^t\),那么先手取的个数为 \(a_i - (\mathop{\oplus}{j\ne i} a_j)\oplus 2^t \pmod {2^{t+1}}\)。由于必胜,后手不能取到 \(2^t\),于是自己
这次取的个数也必须小于 \(2^t\)。枚举 \(t\) 依次判断即可。注意处理 \(t=\infty\)(取完之后异或和归零)
的情况。

我的理解比较浅层,但是算一点:

  • 对于和为偶数的情况,所有选取的答案一定为偶数,奇数同理。

  • 对于奇数情况,必胜顺序一定是第一次先手选一个奇数,然后后手开始选偶数,即上述两种情况可以化为一起。

  • 对于偶数的选取情况,若 \(\displaystyle\sum \lfloor \frac{a_i}{2}\rfloor\) 为偶数,则选取的数一定是 \(4\) 的倍数,否则模 \(4\)\(2\)

但是我至今没有理解为什么取偶数个都会取偶数个

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,k;
inline int lowbit(int x)
{if(x==0) return 1e9;return x&-x;
}
const int N=5e4+6;
int *a;
signed main()
{freopen("nim.in","r",stdin);freopen("nim.out","w",stdout);ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);int sum=0;cin>>n>>k;a=new int [n+2];for(int i=1;i<=n;i++){cin>>a[i],sum^=a[i];}if(lowbit(sum)>k) return cout<<0,0;cout<<"1\n";for(int i=1;i<=n;i++){int now=0,r=sum^a[i];while(114){bool flag=0;for(int j=0;j<=30;j++){if(now+(1<<j)>min(a[i],k)) continue;int kk=now+(1<<j);if(lowbit((a[i]-kk)^r)>kk){now=kk,flag=1;break;}}if(!flag) break;cout<<i<<" "<<now<<"\n";}}
} 

C 均衡区间

首先,处理所有数左边第一个大于它的和小于它的数是显然的。贡献会加在这两个点 min 的外面。

然后就有一个跑的极快的假做法,虽然会被单调序列卡到 \(n^2\)

如下。每次跳最大和最小的位置,然后更新答案。用补集的形式搞,所以不用处理合法的答案。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+6;
inline int read()
{register int s=0;register char c=getchar();while(c<'0'||c>'9') c=getchar();while(c>='0'&&c<='9') s=(s<<1)+(s<<3)+(c^48),c=getchar();return s;
}
void write(int x)
{if(x>9) write(x/10);putchar(x%10+'0');
}
int n,id,a[N],ans[2][N],mx[N],mi[N],st1[N],st2[N],top1,top2,b[N];
signed main()
{freopen("interval.in","r",stdin);freopen("interval.out","w",stdout);n=read(),id=read();if(id==2){for(int i=1;i<=n;i++) putchar('0'),putchar(' ');putchar(10);for(int i=1;i<=n;i++) putchar('0'),putchar(' ');putchar(10);return 0;}for(int i=1;i<=n;i++) a[i]=read(),ans[0][i]=n-i+1,ans[1][i]=i;for(int i=n;i;i--){while(top1&&a[st1[top1]]<a[i]) mx[st1[top1--]]=i;while(top2&&a[st2[top2]]>a[i]) mi[st2[top2--]]=i;st1[++top1]=i,st2[++top2]=i;}for(int i=1;i<=n;i++){int j=min(mx[i],mi[i]);if(!j)j++;ans[1][i]-=i-j+1;--b[j],++b[i+1];int k=mx[i];while(k>=j) k=mx[k];while(k) ans[1][i]--,ans[0][k]--,k=mx[k];k=mi[i];while(k>=j) k=mi[k];while(k) ans[1][i]--,ans[0][k]--,k=mi[k];}for(int i=1;i<=n;i++){b[i]+=b[i-1];write(ans[0][i]+b[i]),putchar(' ');}putchar(10);for(int i=1;i<=n;i++){write(ans[1][i]),putchar(' ');}
}

正解其实就是把上面的处理看作二维数点问题,用树状数组解决。

代码来自 why
#include<bits/stdc++.h>
#define N 1000005
#define pb push_back
using namespace std;
int n,id;
int a[N];
int L[N],R[N],stk[N],tot;
vector<int> s[N],t[N];
struct BIT
{int c[N];void add(int x,int y) {while(x <= n){c[x] += y; x += (x & (-x));}}int sum(int x){int res = 0;while(x) {res += c[x];x -= (x & (-x));}return res;}void cl() {memset(c,0,sizeof(c));}
}B;
int main()
{freopen("interval.in","r",stdin);freopen("interval.out","w",stdout);scanf("%d%d",&n,&id);for(int i = 1;i <= n;i++) scanf("%d",&a[i]);for(int i = 1;i <= n;i++){while(tot && a[i] <= a[stk[tot]]) tot--;L[i] = stk[tot] + 1;stk[++tot] = i;}tot = 0;for(int i = 1;i <= n;i++){while(tot && a[i] >= a[stk[tot]]) tot--;L[i] = min(L[i],stk[tot] + 1);stk[++tot] = i;}tot = 0;stk[tot] = n + 1;for(int i = n;i >= 1;i--){while(tot && a[i] >= a[stk[tot]]) tot--;R[i] = stk[tot] - 1;stk[++tot] = i;}tot = 0;stk[tot] = n + 1;for(int i = n;i >= 1;i--){while(tot && a[i] <= a[stk[tot]]) tot--;R[i] = max(R[i],stk[tot] - 1);stk[++tot] = i;}
//	for(int i = 1;i <= n;i++) printf("%d %d\n",L[i],R[i]);int res = 0;for(int i = 1;i <= n;i++) s[L[i]].pb(i),t[i].pb(i);for(int i = 1;i <= n;i++){for(int j : s[i]) B.add(j,1),res++;int buok = res - (B.sum(R[i]) - B.sum(i - 1));//总数减去合法部分 int ans = (n - R[i]) - buok;printf("%d ",ans);for(int j : t[i]) B.add(j,-1),res--;}printf("\n");res = 0;B.cl();for(int i = 1;i <= n;i++) s[i].clear(),t[i].clear();for(int i = 1;i <= n;i++) s[i].pb(i),t[R[i]].pb(i);for(int i = 1;i <= n;i++){for(int j : s[i]) B.add(j,1),res++;int buok = res - (B.sum(i) - B.sum(L[i] - 1));int ans = (L[i] - 1) - buok;printf("%d ",ans);for(int j : t[i]) B.add(j,-1),res--;}return 0;
}

D 禁止套娃

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

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

相关文章

多校A层冲刺NOIP2024模拟赛24

多校A层冲刺NOIP2024模拟赛24\(T1\) A. 选取字符串 \(100pts\)考虑建出失配树,然后等价于询问 \(\sum\limits_{S \sube \{ 0,1,2, \dots ,n \},|S|=k}dep_{\operatorname{LCA}\{ S \}}^{2}\) 。不妨从 \(\operatorname{LCA}\) 的角度考虑,统计 \(x\) 能作为多少个 \(|S|\) 的…

在微信中使用AI聊天机器人

微信是中国最流行的社交通讯软件,具有庞大的用户基础。ChatGPT是由 OpenAI 开发的、当前最先进的AI聊天机器人,ChatGPT 尤其是在理解和生成自然对话方面表现出色,能够进行流畅且连贯的交流。对中国人而言,将 ChatGPT 集成到微信中,可以在一个熟悉的环境中体验到最新的人工…

protodep踩坑

在使用微服务框架go-zero时,服务拆分比较多,每更新一个服务的proto文件,都要手动复制pb文件到调用的服务里面,新系统开发的时候决定用protodep解决这个问题。 protodep 是一款专为Protocol Buffers接口描述语言文件设计的依赖管理工具。它解决了在使用gRPC时,如何有效控制…

虚幻4 蓝图无法保存 解决方法

蓝图类只能存放在 Blueprints文件夹内,创建在其他文件夹的蓝图类无法保存。 可以看到无法将蓝图拖动到其他文件夹内。这是一个存放在其他文件夹下的蓝图类:当尝试按Ctrl+shift+A保存时,将出现如下错误:解决方法是,将当前蓝图删除(注意,如果该蓝图从某个C++类继承而来,那…

理解进程调度时机跟踪分析进程调度与进程切换的过程

张晓攀+原创作品转载请注明出处+《Linux内核分析》MOOC课程https://mooc.study.163.com/course/1000029000 实验八——理解进程调度时机跟踪分析进程调度与进程切换的过程 一、理解Linux系统中进程调度的时机 在 Linux 内核中,schedule() 函数是核心的进程调度机制。它的主要作…

李继刚Lisp提示词灵感之源:压缩推动进步

探秘李继刚Lisp提示词压缩表达的灵感来源:德国计算机科学家尤尔根施密德胡伯提出,智能系统通过学习新技能来更高效地预测或压缩信息,这种内在动力推动了好奇心和创造力的发展,适用于从婴儿探索世界到科学家发现新规律的各种场景。前面在文章《 访谈李继刚:从哲学层面与大模…

java:找不到符号 符号:变量:log

原文链接:https://blog.csdn.net/zhanghaoninhao/article/details/129180810问题:java:找不到符号 符号:变量:log环境:springboot idea解决方法:在idea中,点击file-Settings,打开配置页面,如图红框位置,输入: -Djps.track.ap.dependencies=false

【SolidWorks 2024下载与安装教程】

‌SolidWorks 2024是一款由达索系统(Dassault Systemes)开发的三维CAD软件,广泛应用于机械设计、产品开发、工程设计、制造等领域。‌ 该软件以其强大的功能和易学易用的特点,深受工程师和设计师的喜爱。SolidWorks 2024在2024版本中引入了一系列新功能和改进,旨在提高设计效…

20222315 2024-2025-1 《网络与系统攻防技术》实验五实验报告

1、实验内容 1)从www.besti.edu.cn、baidu.com、sina.com.cn中选择一个DNS域名进行查询,获取如下信息: DNS注册人及联系方式 该域名对应IP地址 IP地址注册人及联系方式 IP地址所在国家、城市和具体地理位置 PS:使用whois、dig、nslookup、traceroute、以及各类在线和离线工…

编程命名规范(网文)

一个好的变量或函数命名,应该能起到自解释的作用,甚至能减少我们代码的注释。 naming-cheatsheet是一个命名备忘录,记录一些常见的规范约定,并提供简单的例子说明。如果能够严格遵守这些规范,相信我们的代码可读性会大大提升,下面就来介绍 naming-cheatsheet 提供的一些建…

MyBatis 学习笔记

MyBatis 执行器 JDBC 的执行过程分为四步:获取数据库连接(Connection) 预编译 SQL(PrepareStatement) 设置参数 执行 SQL(ResultSet)MyBatis 提供了执行器 Executor 将这一过程进行封装,对外提供 SqlSession 让用户通过调用其 API 直接操作数据库,因为 SqlSession 持有…