100+0+99=199,第二题一分没得不应该。

count

给定 \(n\),求合法的 \((x_1,x_2,x_3,\dots,x_{2m})\) 组数。一组 \(x\) 是合法的,当且仅当

\[\forall i\in[1,2m],x_i\in \mathrm{Z}_+,x_i\mid n \\
\prod_{i=1}^{2m}x_i=n^m
\]

合法的 \((x_1,x_2,x_3,\dots,x_{2m})\) 可能有很多,请你输出方案数 \(\bmod 998244353\)。

\(n \leq 10^9,m \leq 100\)。

题解

看了好久才找到正确的切入点。

一开始我把所有数取 \(\log_n\),想把乘法变成加法。然后发现这样做有精度误差,实际效果跟 DFS 没什么区别。

然后我突然想到把每个 \(x_i\) 变成 \(\frac{n}{x_i}\),乘积恰好反过来了。于是答案是所有情况除以 \(2\)?稍加思考便发现这不太对,可能有等于 \(\prod x_i=n^m\) 的情况。所以问题转化成了等于符号。

求解这个问题我也是费了一番周折。首先想到的是质因子分开算最后可以用乘法原理合并。

然后我以为对每个质因子都是一个组合数。假设某个质因子的形式是 \(p^\alpha\) ,那么答案不就是 \(m\alpha\) 拆成 \(2m\) 个自然数的方案数吗?我熟练地写出答案 \(\binom{m\alpha+2m-1}{2m-1}\),过了小样例。

然后开始手动和我一开始写的暴力对拍,发现答案错了。比如这组数据:16 1。调了半天发现是每个拆分的指数 \(y\) 必须满足 \(0 \leq y \leq \alpha\)。有点自闭。

最后改成 DP 了,然后又找了几个小错,总算两个程序拍上了。

#include<bits/stdc++.h>
#include<tr1/unordered_map>
using namespace std;
template<class T> T read(){
T x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x;
}
template<class T> T read(T&x){
return x=read<T>();
}
#define co const
#define il inline
typedef long long LL; co int mod=998244353,i2=499122177;
il int add(int a,int b){
return (a+=b)>=mod?a-mod:a;
}
il int mul(int a,int b){
return (LL)a*b%mod;
}
il int fpow(int a,int b){
int ans=1;
for(;b;b>>=1,a=mul(a,a))
if(b&1) ans=mul(ans,a);
return ans;
} int f[201][3001];
int dp(int n,int m,int lim){
// cerr<<"n="<<n<<" m="<<m<<endl;
for(int i=0;i<=n;++i) fill(f[i],f[i]+m+1,0);
f[0][0]=1;
for(int i=0;i<n;++i)
for(int j=0;j<=m;++j)if(f[i][j])
for(int k=0;k<=lim&&k<=m-j;++k) f[i+1][j+k]=add(f[i+1][j+k],f[i][j]);
return f[n][m];
}
int main(){
freopen("count.in","r",stdin),freopen("count.out","w",stdout);
int n=read<int>(),m=read<int>(); int ans=0;
for(int i=1;i*i<=n;++i)if(n%i==0){
++ans;
if(n/i!=i) ++ans;
}
ans=fpow(ans,2*m); int nn=n,sum=1;
for(int i=2;i*i<=n;++i)if(nn%i==0){
int c=0;
while(nn%i==0) nn/=i,++c;
sum=mul(sum,dp(2*m,m*c,c));
}
if(nn>1) sum=mul(sum,dp(2*m,m,1));
// cerr<<"ans="<<ans<<" sum="<<sum<<endl;
printf("%d\n",mul(add(ans,sum),i2));
return 0;
}

delete

给定一个序列,你需要通过不断的操作来消除这个序列。每次你可以选择一个上升或者下降子序列将其删除,将剩下的序列作为新序列,继续删除操作。直到新序列为空,操作结束。

具体地说,一次操作可以表述为如下较为形式的语言:对于一个序列 (x1, x2, ..., xn),一次操作可以选择 1 ≤ a1 < a2 < ... < ak ≤ n,将 xa1, xa2, ..., xak 删去。其中 k 和 ai 都是你可以决定的,但是须要满足 xa1 < xa2 < ... < xak,或者 xa1 > xa2 > ... > xak

我们希望你在 500 次内将序列删空,请你输出任意一种删除序列的方式,数据保证一定存在解。

为了方便起见,保证 (x1, x2, ..., xn) 是一个 1..n 的排列。

n ≤ 64000。

题解

观察一番 SPJ 后终于知道我为什么爆 0 了。他要求输出的是元素,也就是 x 的值,而不是位置。

贪心找可还行。Dilworth 定理是什么?

Dilworth 定理

Dilworth 定理可用来优化序列的不下降子序列最少划分数。

相关定义

偏序关系是满足自反性、反对称性、传递性的二元关系。可以用 ≤ 表示。

  • 自反性:x ≤ x成立。
  • 反对称性:a ≤ b 且 b ≤ a ↔ a=b
  • 传递性:a ≤ b 且 b ≤ c → a ≤ c

配备偏序关系的集合称为偏序集。显然数集上的“小于或等于”是偏序关系,但是偏序关系不只可以在数集上。

  • 可比:a与b可比,当且仅当a ≤ b 或 b ≤ a。

设全集U是一个偏序集。

  • :U的子集,满足其中任意两个元素(不相同)可比。
  • 反链:U的子集,满足其中任意两个元素(不相同)不可比。

比如,有限数集S上的最长链的长度等于|S|,最长反链长度为1.

将U分拆成很多子集称作划分。姑且把子集全为链的划分叫做链划分,全为反链叫反链划分。

定理内容

有两个互为对偶的定理:

  1. U的链划分使用的最少集合数,等于它的最大反链长度。
  2. U的反链划分使用的最少集合数,等于它的最大链长度。

其中某一个叫做 Dilworth 定理

回到那个问题:给定一个全为实数的序列,每次在其中选出一个不下降子序列并将其删掉。求删完整个序列所需要的选择子序列的次数。比如说,1 2 4 3 5这个序列,最少要两次,第一次1 2 3 5.

虽然序列与集合不同,但这很可能与最少划分有关。不管怎么说,打算用Dilworth定理相关内容解决本题。

首先要把序列转化成集合。不同点在于序列中元素有序。考虑使用有序数对 (p,v) 表示原序列中的一个数。p 表示位置,v 表示数值。

从集合中任意取出两个元素,如何能确定前者不比后者大呢?考虑关系 ≤:p1 ≤ p1 且 v1 ≤ v2。这相当于先把两个元素按照原序列中的位置排序,再比较数值大小。这个关系是偏序关系。如此一来,两个元素可比,等价于在原序列中,他们是不下降的。

根据 (1),原题所求的次数,等于原序列的最长下降子序列的长度。

反过来下降子序列的最小划分数,等于最长不下降子序列的长度。


刘老爷写了一个链表+线段树然后 TLE 了。看来这题卡常数。

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <memory.h>
#include <vector> using namespace std;
typedef long long LL; const int maxn = 64005;
#define pb push_back int a[maxn],a0[maxn],n,tot,f[maxn],g[maxn];
vector<int> vec[maxn];
int mx1[maxn],mx2[maxn],bac[maxn],del[maxn]; void Print()
{
printf("%d\n",tot);
for (int i=1;i<=tot;i++)
{
int len=vec[i].size();
printf("%d ",len);
for (int j=len-1;j>=0;j--)
printf("%d%c",vec[i][j]," \n"[j==0]);
}
}
void insert1(int x,int v) {
for (int i=x;i<=n;i+=i&-i)
mx1[i]=max(mx1[i],v);
}
void insert2(int x,int v) {
for (int i=x;i;i-=i&-i)
mx2[i]=max(mx2[i],v);
}
int query1(int x) {
int res=0;
for (int i=x;i;i-=i&-i)
res=max(res,mx1[i]);
return res;
}
int query2(int x) {
int res=0;
for (int i=x;i<=n;i+=i&-i)
res=max(res,mx2[i]);
return res;
} void DP()
{
++tot;
memset(mx1,0,n+1<<2);
memset(mx2,0,n+1<<2);
int ans1=0,ans2=0;;
for (int i=1;i<=n;i++)
{
f[i]=query1(a[i])+1;
g[i]=query2(a[i])+1;
insert1(a[i],f[i]);
insert2(a[i],g[i]);
ans1=max(ans1,f[i]);
ans2=max(ans2,g[i]);
} memset(del,0,n+1<<2);
if (ans1>ans2) {
for (int t=ans1,i=n;t;i--)
if (f[i]==t) del[a[i]]=1,t--,vec[tot].pb(a0[i]);
}
else {
for (int t=ans2,i=n;t;i--)
if (g[i]==t) del[a[i]]=1,t--,vec[tot].pb(a0[i]);
}
for (int i=1;i<=n;i++)
bac[i]=bac[i-1]+1-del[i];
int pos=0;
for (int i=1;i<=n;i++)
if (!del[a[i]]) a0[++pos]=a0[i],a[pos]=bac[a[i]];
n=pos;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("delete.in","r",stdin);
freopen("delete.out","w",stdout);
#endif
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]),a0[i]=a[i];
while (n) DP();
Print();
return 0;
}

floor

令 \(x=\frac{\sqrt{5}+1}{2}\),求 \(\lfloor x^n \rfloor \mod p\)。

\(n \leq 10^{18},p \leq 998244353\)。

题解

酷似 JLOI2015 有意义的字符串 。先找到对应的数列,尝试几番就发现

\[\left(\frac{1+\sqrt{5}}{2}\right)^n+\left(\frac{1-\sqrt{5}}{2}\right)^n
\]

这个表达式对应了数列 \(1,3,4,7,11,18\dots\)。然后写个矩阵快速幂即可求出这个数列。

注意根据后一个小数的符号判断答案要不要减 \(1\)。

#include<bits/stdc++.h>
using namespace std;
template<class T> T read(){
T x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x;
}
template<class T> T read(T&x){
return x=read<T>();
}
#define co const
#define il inline
#define int long long int mod;
struct mat{
int v[2][2];
il int*operator[](int i) {return v[i];}
il co int*operator[](int i)co {return v[i];}
};
mat operator*(co mat&a,co mat&b){
mat c;memset(c.v,0,sizeof c.v);
for(int i=0;i<2;++i)
for(int j=0;j<2;++j)
for(int k=0;k<2;++k)
c[i][j]=(c[i][j]+a[i][k]*b[k][j])%mod;
return c;
}
mat pow(mat a,int b){
mat ans;memset(ans.v,0,sizeof ans.v);
for(int i=0;i<2;++i) ans[i][i]=1;
for(;b;b>>=1,a=a*a)
if(b&1) ans=ans*a;
return ans;
} signed main(){
freopen("floor.in","r",stdin),freopen("floor.out","w",stdout);
int n=read<int>();read(mod);
if(n==0) {puts("1");return 0;} // edit 1
mat a=pow((mat){0,1,1,1},n-1);
int ans=(a[0][0]+3*a[0][1])%mod;
if(~n&1) ans=(ans+mod-1)%mod;
printf("%lld\n",ans);
return 0;
}

test20190825 AmberFrame的更多相关文章

  1. JZOJ 5812. 【NOIP提高A组模拟2018.8.14】 区间

    5812. [NOIP提高A组模拟2018.8.14] 区间 (File IO): input:range.in output:range.out Time Limits: 1000 ms  Memo ...

  2. BZOJ1101 [POI2007]Zap 和 CF451E Devu and Flowers

    Zap FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d.作为FGD的同学,FGD希望得到 ...

随机推荐

  1. Postman系列四:Postman接口请求设置环境变量和全局变量、测试沙箱和测试断言、测试集运行与导入数据文件

    一:Postman中接口请求设置环境变量和全局变量 全局变量和环境变量可以通过Pre-request Script和Tests设置,会在下面测试沙箱和测试断言中讲到. 全局变量的设置:官网参考http ...

  2. JVM之java并发 ——线程安全与锁优化

    概述 人们很难想象现实中的对象在一项工作进行期间,会被不停地中断和切换,对象的属性(数据)可能会在中断期间被修改和变“脏”,而这些事情在计算机世界中则是很正常的事情.有时候,良好的设计原则不得不向现实 ...

  3. liunx 定时任务执行java程序配置流程

    java jar包使用build fat jar进行打包 ------------------liunx任务创建--------------------------- 1.查看现有任务计划: cron ...

  4. python学习67-面向对象-封装

    封装 1.什么是封装? 根据名字寓意为:把一个东西装起来,然后密封,类似这样的面向对象的编程为封装. 真正的封装是明确的区别内外,只能在内部用,外部无法调用. 2. 举例: class Car: _s ...

  5. 用Python写一个滑动验证码

    1.准备阶段 滑动验证码我们可以直接用GEETEST的滑动验证码. 打开网址:https://www.geetest.com/ ,找到技术文档中的行为验证,打开部署文档,点击Python,下载ZIP包 ...

  6. WAMP集成环境虚拟路径修改

    只需要改httpd.conf这一个文件就好了. 1.单击右下角wamp图标如下图打开httpd.conf,或者从文件夹打开httpd.conf.

  7. TypeScript之枚举

    什么是枚举类型,有什么作用? 枚举类型就是一个用来组织一些有相似之处的常量的对象,作用就是管理常量,让常量更规范,统一.例: enum Direction { Up = 1, Down, Left, ...

  8. tomcat线程池调优

    之前项目一直在tomcat下开发,后来在上线之前,需要进行性能安全测试,可是测试的同事反应,登陆口线程并发一多的时候,系统立马就没法登陆了. 中间件是tomcat6.  tomcat的日志总是简洁的很 ...

  9. aspnetcore 容器化部属到阿里云全过程记录

    第一次写博客,作为一个全栈er,记录一下从阿里云到产品运维上线的全过程 一.阿里云上的设置 购买阿里云ECS后: 进控制台查看实例公网IP 在控制台.网络与安全->安全组,配置规则 点击进去可以 ...

  10. 在Unity中创建VR游戏

    添加VR插件为了为您选择的平台创建VR游戏,我们需要下载几个插件.出于本教程的目的,我将向您展示如何上传到Android平台.要上传到iOS,您需要下载 Xcode. 现在让我们下载Unity的Goo ...