JXOI2018简要题解

T1 排序问题

题意

九条可怜是一个热爱思考的女孩子。

九条可怜最近正在研究各种排序的性质,她发现了一种很有趣的排序方法: Gobo sort !

Gobo sort 的算法描述大致如下:

  1. 假设我们要对一个大小为 \(n\) 的数列 \(a\) 排序。
  2. 等概率随机生成一个大小为 \(n\) 的排列 \(p\) 。
  3. 构造一个大小为 \(n\) 的数列 \(b\) 满足 \(b_i=a_{p_i}\) ,检查 \(b\) 是否有序,如果 \(b\) 已经有序了就结束算法,并返回 \(b\) ,不然返回步骤 \(2\) 。

显然这个算法的期望时间复杂度是 \(O(n\times n!)\) 的,但是九条可怜惊奇的发现,利用量子的神奇性质,在量子系统中,可以把这个算法的时间复杂度优化到线性。

九条可怜对这个排序算法进行了进一步研究,她发现如果一个序列满足一些性质,那么 Gobo sort 会很快计算出正确的结果。为了量化这个速度,她定义 Gobo sort 的执行轮数是步骤 \(2\) 的执行次数。

于是她就想到了这么一个问题:

现在有一个长度为 \(n\) 的序列 \(x\) ,九条可怜会在这个序列后面加入 \(m\) 个元素,每个元素是 \([l,r]\) 内的正整数。

她希望新的长度为 \(n+m\) 的序列执行 Gobo sort 的期望执行轮数尽量的多。她希望得到这个最多的期望轮数。

九条可怜很聪明,她很快就算出了答案,她希望和你核对一下,由于这个期望轮数实在是太大了,于是她只要求你输出对 \(998244353\) 取模的结果。

对于 \(30\%\) 的数据, \(T\leq 10\) , \(n,m,l,r\leq 8\)。

对于 \(50\%\) 的数据, \(T\leq 300,n,m,l,r,a_i\leq 300\) 。

对于 \(60\%\) 的数据, \(\sum{r-l+1}\leq 10^7\) 。

对于 \(70\%\) 的数据, \(\sum{n} \leq 2\times 10^5\) 。

对于 \(90\%\) 的数据,\(m\leq 2\times 10^5\)。

对于 \(100\%\) 的数据, \(T\leq 10^5,n\leq 2\times 10^5,m\leq 10^7,1\leq l\leq r\leq 10^9\) 。

对于 \(100\%\) 的数据, \(1\leq a_i\leq 10^9,\sum{n}\leq 2\times 10^6\) 。

题解

一道不错的签到题。

首先如果每个数都不同,画一画可以知道,有且只有唯一的一种排列是满足条件的,此时需要\(n!\)次。

但是有数相同,那么我们可以强制他们有大小关系、每一种大小关系对应一种排列。如果相同的数个数分别是\(a_1,a_2...a_k\),则答案为\(\frac{n!}{a_1!a_2!a_3!...a_k!}\)

考虑怎么样加数会使得答案最大:肯定越平均越大(优先加个数少的数字,因为乘的分母小)。于是就维护一个类似阶梯的东西贪心地加就好了。

代码

#include<iostream>
#include<algorithm>
using namespace std;
const int mod=998244353,N=2e5+10;
int n,m,l,r,jc[11000000],a[N],b[N],c[N],t[N];
int ksm(int x,int k)
{
int s=1;for(;k;k>>=1,x=1ll*x*x%mod)
if(k&1) s=1ll*s*x%mod;return s;
}
void Work()
{
scanf("%d%d%d%d",&n,&m,&l,&r);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1);
int ans=jc[n+m],t1=0,t2=0,top=0;
for(int i=1;i<=n;i++)
if(a[i]>=l&&a[i]<=r) b[++t1]=a[i];
else c[++t2]=a[i];
b[t1+1]=c[t2+1]=0;
for(int i=2,res=1;i<=t2+1;i++)
if(c[i]!=c[i-1]) ans=1ll*ans*ksm(jc[res],mod-2)%mod,res=1;
else res++;
for(int i=2,res=1;i<=t1+1;i++)
if(b[i]!=b[i-1]) t[++top]=res,res=1;
else res++;
sort(t+1,t+top+1);
int s=r-l+1-top,nw=0,i=1;
for(;i<=top;i++)
if(1ll*s*(t[i]-nw)<=m)
m-=1ll*s*(t[i]-nw),nw=t[i],s++;
else break;
nw+=m/s;m%=s;
ans=1ll*ans*ksm(ksm(jc[nw+1],mod-2),m)%mod;
ans=1ll*ans*ksm(ksm(jc[nw],mod-2),s-m)%mod;
for(;i<=top;i++) ans=1ll*ans*ksm(jc[t[i]],mod-2)%mod;
printf("%d\n",ans);
}
int main()
{
int T;cin>>T;jc[0]=1;
for(int i=1;i<=1e7+1e6;i++) jc[i]=1ll*jc[i-1]*i%mod;
while(T--) Work();
}

T2 游戏

题意

九条可怜是一个富有的女孩子。她长大以后创业了,开了一个公司。

但是管理公司是一个很累人的活,员工们经常背着可怜偷懒,可怜需要时不时对办公室进行检查。

可怜公司有 \(n\) 个办公室,办公室编号是 \(l\sim l+n-1\) ,可怜会事先制定一个顺序,按照这个顺序依次检查办公室。一开始的时候,所有办公室的员工都在偷懒,当她检查完编号是 \(i\) 的办公室时候,这个办公室的员工会认真工作,并且这个办公室的员工通知所有办公室编号是 \(i\) 的倍数的办公室,通知他们老板来了,让他们认真工作。因此,可怜检查完第 \(i\) 个办公室的时候,所有编号是 \(i\) 的倍数(包括 \(i\) )的办公室的员工会认真工作。

可怜发现了员工们通风报信的行为,她发现,对于每种不同的顺序 \(p\) ,都存在一个最小的 \(t(p)\) ,使得可怜按照这个顺序检查完前 \(t(p)\) 个办公室之后,所有的办公室都会开始认真工作。她把这个 \(t(p)\) 定义为 \(p\) 的检查时间。

可怜想知道所有 \(t(p)\) 的和。

但是这个结果可能很大,她想知道和对 \(10^9+7\) 取模后的结果。

对于 \(20\%\) 的数据,\(r-l+1\leq 8\)。

对于另 \(10\%\) 的数据,\(l=1\)。

对于另 \(10\%\) 的数据,\(l=2\)。

对于另 \(30\%\) 的数据,\(l\leq 200\)。

对于 \(100\%\) 的数据,\(1\leq l\leq r\leq 10^7\)。

题解

一道简单的组合计数问题。

如果把倍数关系画成一张拓扑图的话,那么当且仅当入度为0的点都被选了,检查结束。

设一共有k个入度为0的点,考虑选多少次能够选全,答案就是

\[\sum_{i=k-1}^{n-1}(i+1)C_{i}^{k-1}k!(n-k)!
\]

含义是在前\(i\)次中选了\(k-1\)个,在第\(i+1\)次中选了剩下的那一个。贡献是\(i+1\),在前\(i\)次中选出\(k-1\)次决策用来选必选点,把每一种决策是否选必选点分配好后、只需要把必选点和非必选点排列上去就是方案了,为\(k!(n-k)!\)。

在找入度为0的点是用线性筛,找到自己在\([l,r]\)且为质数或最大因数不在\([l,r]\)的数。

代码

#include<iostream>
using namespace std;
const int N=1e7+10,mod=1e9+7;
int l,r,n,Ans,cnt,ispri[N],pri[N],jc[N],inv[N],tot;
int ksm(int x,int k)
{
int s=1;for(;k;k>>=1,x=1ll*x*x%mod)
if(k&1) s=1ll*s*x%mod;return s;
}
void Pre()
{
ispri[1]=pri[1]=1;
if(l==1) {cnt=1;return;}
for(int i=2;i<=r;i++)
{
if(!ispri[i]) pri[++tot]=i,cnt+=(i>=l);
for(int j=1;j<=tot&&i*pri[j]<=r;j++)
{
ispri[i*pri[j]]=1;
if(i<l&&i*pri[j]>=l) cnt++;
if(i%pri[j]==0) break;
}
}
}
int main()
{
cin>>l>>r;n=r-l+1;Pre();jc[0]=inv[0]=1;
for(int i=1;i<=r;i++) jc[i]=1ll*jc[i-1]*i%mod;
inv[r]=ksm(jc[r],mod-2);
for(int i=r-1;i>=1;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
for(int i=cnt-1;i<=n-1;i++)
(Ans+=1ll*jc[i]*inv[i-cnt+1]%mod*(i+1)%mod)%=mod;
Ans=1ll*Ans*cnt%mod*jc[n-cnt]%mod;
cout<<Ans<<endl;
}

T3 守卫

题意

九条可怜是一个热爱运动的女孩子。

这一天她去爬山,她的父亲为了她的安全,雇了一些保镖,让他们固定地呆在在山的某些位置,来实时监视九条可怜,从而保护她。

具体来说,一座山可以描述为一条折线,折线的下方是岩石。这条折线有 \(n\) 个折点,每个折点上有一个亭子,第 \(i\) 个折点的坐标是 \((i,h_i)\) 。九条可怜只可能会在亭子处玩耍,那些保镖也只会在亭子处监视可怜。

由于技术方面的原因,一个保镖只能监视所有他能看得到的,横坐标不超过他所在位置的亭子。我们称一个保镖能看到一个亭子 \(p\) ,当且仅当他所在的亭子 \(q\) 和 \(p\) 的连线不经过任何一块岩石。特别地,如果这条连线恰好经过了除了 \(p,q\) 以外的亭子,那么我们认为保镖看不到可怜。

雇佣保镖是一件很费钱的事情,可怜的父亲希望保镖越少越好。

可怜的父亲还希望得到详尽的雇佣保镖的方案,他知道有些亭子可能正在维修,他想对所有的 \(1\leq l\leq r\leq n\) 计算:如果事先已知了只有区间 \([l,r]\) 的亭子可以用来玩耍(和监视),那么最少需要多少个保镖,才能让 \([l,r]\) 中的每一个亭子都被监视到。

可怜的父亲已经得到了一个结果,他希望和你核实他的结果是否正确。

对于 \(30\%\) 的数据, \(n\leq 20\) 。

对于 \(70\%\) 的数据, \(n\leq 500\) 。

对于 \(100\%\) 的数据, \(n\leq 5000\) 。

对于 \(100\%\) 的数据, \(1\leq h_i\leq 10^9\) 。

题解

略有难度的DP。

首先可以维护斜率最值来得到两点间的可见关系。(一开始以为可见区域一定是一段区间,搞了好久的贪心结果错了,Hack:5 1 4 5)

然后设\(f[l][r]\)表示这个区间的最少关键点数,那么一定要在\(r\)处放置一个,再依次找\(r\)看不到的极大区间\([l1,r1]\),考虑在\(r1,r1+1\)中选一个点作为关键点就好了。

一个需要解释的地方就是为什么\([r1+2,r]\)都看不到\([l1,r1]\):因为\(r\)看得到\(r1+1\)和\(r1+2\),所以\(r\)和\(r1+2\)连线的夹角要大一些、在\(r\)和\(r1+1\)连线下方;如果\(r1+2\)看得到\(r1\)的话,则\(r1+2\)和\(r1+1\)的连线的夹角比\(r1+2\)和\(r1\)的大。夹角均为x正半轴出发的有向角,如此一画发现矛盾,\(r1+2\)看不到\(r1\)。

代码很简单,固定一个\(r\),从后往前扫\(l\)。

代码

#include<iostream>
using namespace std;
const int N=5100;
int n,h[N],f[N][N],see[N][N],ans;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>h[i];
for(int i=1,l=0;i<=n;i++,l=i-1)
for(double mn=1e9;l>=1;l--)
if(1.0*(h[i]-h[l])/(i-l)<mn) mn=1.0*(h[i]-h[l])/(i-l),see[l][i]=1;
for(int r=1;r<=n;r++)
for(int l=r,s=1,lst=l;l>=1;l--)
{
if(see[l][r])
{
if(!see[l+1][r]) s+=min(f[l+1][lst],f[l+1][lst+1]);
f[l][r]=s;
}
else
{
if(see[l+1][r]) lst=l;
f[l][r]=s+min(f[l][lst],f[l][lst+1]);
}
ans^=f[l][r];
}
cout<<ans<<endl;
}

后记

听说JXOI 80分就能进队什么鬼啊。。

这放在HN那不晓得有多少AK的。。

我做起来还是比较轻松吧,100+100+30。当然T2推式子的时候有一项推错了,瞟一眼题解发现这题确实是推式子才继续把它推对;T3xjb贪心过了前三个点。这些都是考场上三个半小时不一定能写出来的,所以说虽然80分可以进队,但真正如果是考试,自己又能不能不失误呢?还需要斟酌、修炼。

JXOI2018简要题解的更多相关文章

  1. Noip 2014酱油记+简要题解

    好吧,day2T1把d默认为1也是醉了,现在只能期待数据弱然后怒卡一等线吧QAQ Day0 第一次下午出发啊真是不错,才2小时左右就到了233,在车上把sao和fate补掉就到了= = 然后到宾馆之后 ...

  2. Tsinghua 2018 DSA PA2简要题解

    反正没时间写,先把简要题解(嘴巴A题)都给他写了记录一下. upd:任务倒是完成了,我也自闭了. CST2018 2-1 Meteorites: 乘法版的石子合并,堆 + 高精度. 写起来有点烦貌似. ...

  3. Codeforces 863 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 简要题解?因为最后一题太毒不想写了所以其实是部分题解... A题 传送门 题意简述:给你一个数,问你能不能通过加前导000使其成为一个回文数 ...

  4. HNOI2018简要题解

    HNOI2018简要题解 D1T1 寻宝游戏 题意 某大学每年都会有一次 Mystery Hunt 的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会. 作为 ...

  5. BJOI2018简要题解

    BJOI2018简要题解 D1T1 二进制 题意 pupil 发现对于一个十进制数,无论怎么将其的数字重新排列,均不影响其是不是 \(3\) 的倍数.他想研究对于二进制,是否也有类似的性质. 于是他生 ...

  6. CQOI2018简要题解

    CQOI2018简要题解 D1T1 破解 D-H 协议 题意 Diffie-Hellman 密钥交换协议是一种简单有效的密钥交换方法.它可以让通讯双方在没有事先约定密钥(密码)的情况下,通过不安全的信 ...

  7. AtCoder ExaWizards 2019 简要题解

    AtCoder ExaWizards 2019 简要题解 Tags:题解 link:https://atcoder.jp/contests/exawizards2019 很水的一场ARC啊,随随便便就 ...

  8. Comet OJ - Contest #2 简要题解

    Comet OJ - Contest #2 简要题解 cometoj A 模拟,复杂度是对数级的. code B 易知\(p\in[l,r]\),且最终的利润关于\(p\)的表达式为\(\frac{( ...

  9. HNOI2019 简要题解

    HNOI 2019 简要题解 没想到自己竟也能有机会写下这篇题解呢. LOJ Luogu Day1T1 鱼 枚举\(AD\)两点后发现\(BC\)与\(EF\)相对独立,因此只需要计算合法的\(BC\ ...

随机推荐

  1. QQ浏览器兼容模式问题

    今天客户反馈有个问题,他说用360浏览器的兼容模式无法登陆系统,我试了可以,接着试了IE11,也可以,然后跟经理汇报,他说他用qq浏览器兼容模式就不可以,于是我试了,果然不可以... 问题是酱紫的:输 ...

  2. (后端)NoSuchMethodError

    这个错误是说编译时有这个方法,但运行时没有了 请使用:mvn clean install

  3. tkinter中combobox下拉选择控件(九)

    combobox控件,下拉菜单控件 combobox控件在tkinter中的ttk下 简单的实现下: import tkinter from tkinter import ttk # 导入ttk模块, ...

  4. word中从正文开始编码的方法

    假如第1页和第2页是首页和目录,你想从第3页的正文开始设置页码 1. 将光标移到第二页的最后位置,点击插入“分隔符”,选择“分节类型”的“下一页”,确定.这时光标自动移到第三页. 2. 点击插入页码, ...

  5. MVC model验证 获取验证错误信息

    public static class ModelStateExtensions { /// <summary> /// 获取model验证错误信息 /// </summary> ...

  6. Linux 下查看系统当前登录用户信息

    当你新登录一个主机,过着管理一个主机,这时候你就需要这些命令来进行查看了: 1. w : Show who is logged on and what they are doing. w [optio ...

  7. python UDP套接字通信

    UDPserver.py import socket #导入套接字模块 s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # - socket.A ...

  8. 【PAT】B1038 统计同成绩学生(20)(20 分)

    #include<stdio.h> int arr[102]={0};//分数作为自己的下标,注意 int main(){ int N;scanf("%d",& ...

  9. 【PAT】B1078 字符串压缩与解压(20 分)

    主函数接收下第一个字符,接着一个分支就转到两个函数中的一个 1.压缩简单,只要与下一个一样就只计数,如果不同了就直接输出 2.至于解压不知道数字是几位数,所以我直接用了sscanf,然后判断是几位数字 ...

  10. java通过传入的日期,获取所在周的周一至周日

    public static void main(String[] args) { try { SimpleDateFormat sdf=new SimpleDateFormat("yyyy- ...