P5042 丢失的题面

顺序:10 - 1 - 7 - 8 - 9 - 4 - 5 - 6 - 2 - 3

Point 10

读入,特判,输出。

读入的英文意思是让选手输出自己的程序本身,这个题的确存在,但是这题并没有 SPJ ,所以特判一下输出输出文件就好了。

C++ 的atoi函数可以让读入的字符串变成数字以完成其他点的任务。

Point 1

我和其他聚铑做这个点的过程就十分有趣了。

开始,我们使用了大眼观察法观察了一下这个输出,不知道怎么我就看出了将序列每四个字符划分,然后变成若干以0110开头的长度为 4 的 01 串组。发现这些组的大小都在 3 以内,于是我们在 OEIS 上搜了一下这个序列,居然真的找到了:A007413

它的递推方式是三个元素,每次操作 a->abc b->ac c->b 。这道题中这三个字母分别代表 0110 01101001011010011001 。做了一下,发现是对的。

然后观察文件大小发现输出的字符串长度是 \(2^{22}\) 。没细想,写完就过了。

但是为什么是 \(2^{22}\) 呢?

我回头看了一眼,发现输出好像是初始一个 0 字符,每次操作将当前串取反拼接到后面,做 22 次的结果……(鬼知道我当初为啥直接就想每 4 个分组)

然后又看了下 OEIS,发现这个输出本身就是一个数列:A010060。这个序列还有个名字叫做

Thue-Morse Sequence

wikihow中,叙述了这个序列的几种构造方法,除了上述的两种构造方式之外,还可以直接每次操作 0->01 1->10 来构造。

真有趣

但是代码还是用的第一次写的,懒得改了。(by a___)

namespace subtask1{
int n=22;
vector<vector<int>>vec,tmp;
string s[4],ans;
vector<int>to[4];
inline void main(){
int n=20;
to[1]=vector<int>({1,2,3});
to[2]=vector<int>({1,3});
to[3]=vector<int>({2});
vec.push_back(std::vector<int>({1}));
while(n--)
{
for(auto p:vec)
for(auto q:p)tmp.push_back(to[q]);
swap(vec,tmp);tmp.clear();
}
s[1]="0110";
s[2]="01101001";
s[3]="011010011001";
for(auto p:vec)ans+=s[p.size()];
ans.resize(1<<22);
std::cout<<ans<<std::endl;
}
}

Point 7

因为 2~6 个点都没有什么思路,直接来看第 7 个点。

观察了一下输入,发现是一个图,而且边没有边权。观察了一下输出,发现输出只有 0 和 INF 两种数字。于是几乎可以确定是判断图上两点间连通性了。

namespace subtask7{
int fa[100005];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void onion(int x,int y){
x=find(x),y=find(y);
if(x!=y) fa[x]=y;
}
inline void main(int n,int m){
int q=read();
while(n) fa[n]=n,n--;
while(m--) onion(read(),read());
while(q--) puts(find(read())==find(read())?"0":"2139062143");
}
}

Point 8

切完了上面的点,一看这个点也是个图,而且边有边权。大概扫了一下发现这是个随机生成的树,边权也是随的。询问格式是两个点。

再观察输出,发现输出的答案大多都大于 90000。说明是一个答案期望较大的询问。两点间路径和或者乘积不可能,试验了一下异或发现答案溢出 100000,那么或也顺便排除。

傻了吧唧地想了半天,突然有一位聚铑想到,为啥不是最大值呢?

试了一下果然没错。

namespace subtask8{
const int maxn=1e5+10;
int fa[maxn][21],mx[maxn][21],Q,n=100000;
int ecnt,head[maxn],to[maxn<<1],nxt[maxn<<1],v[maxn<<1],dep[maxn];
inline void addedge(int a,int b){
to[++ecnt]=b,nxt[ecnt]=head[a],head[a]=ecnt;
to[++ecnt]=a,nxt[ecnt]=head[b],head[b]=ecnt;v[ecnt]=v[ecnt-1]=read();
}
void dfs(int x,int f){
fa[x][0]=f;dep[x]=dep[f]+1;
for(int i=0;i<18;i++) fa[x][i+1]=fa[fa[x][i]][i],mx[x][i+1]=max(mx[x][i],mx[fa[x][i]][i]);
for(int i=head[x];i;i=nxt[i]){
int u=to[i];
if(u==f)continue;
mx[u][0]=v[i];
dfs(u,x);
}
}
inline int solve(int x,int y){
if(dep[x]<dep[y])swap(x,y);
int ans=0;
for(int i=18;~i;i--) if(dep[fa[x][i]]>=dep[y]) ans=max(ans,mx[x][i]),x=fa[x][i];
if(x==y)return ans;
for(int i=18;~i;i--) if(fa[x][i]!=fa[y][i]) ans=max(ans,max(mx[x][i],mx[y][i])),x=fa[x][i],y=fa[y][i];
ans=max(ans,max(mx[x][0],mx[y][0]));
return ans;
}
inline void main(){
Q=read();
for(int i=1;i<n;i++) addedge(read(),read());
dfs(1,0);
while(Q--) printf("%d\n",solve(read(),read()));
}
}

Point 9

观察了一下输入,发现是个图。

观察了一下输出,发现有 INF 的存在,那么询问可能是让答案尽量小,所以盲猜最小瓶颈路。试了一下,果然是。

kruskal 的并查集和路径最大值都可以用前面的板子。(出题人真良心)

namespace subtask9{
const int maxn=100005,INF=2139062143;
int Fa[100005],fa[maxn][21],mx[maxn][21];
int find(int x){return Fa[x]==x?x:Fa[x]=find(Fa[x]);}
struct edge{
int u,v,val;
inline bool operator < (const edge& zp) const {return val<zp.val;}
}e[maxn<<1];
int ecnt,head[maxn],to[maxn<<1],nxt[maxn<<1],v[maxn<<1],dep[maxn];
inline void addedge(int a,int b,int c){
to[++ecnt]=b,nxt[ecnt]=head[a],head[a]=ecnt;v[ecnt]=c;
to[++ecnt]=a,nxt[ecnt]=head[b],head[b]=ecnt;v[ecnt]=c;
}
void dfs(int x,int f){
fa[x][0]=f;dep[x]=dep[f]+1;
for(int i=0;i<18;i++) fa[x][i+1]=fa[fa[x][i]][i],mx[x][i+1]=max(mx[x][i],mx[fa[x][i]][i]);
for(int i=head[x];i;i=nxt[i]){
int u=to[i];
if(u==f)continue;
mx[u][0]=v[i];
dfs(u,x);
}
}
inline int solve(int x,int y){
if(dep[x]<dep[y])swap(x,y);
int ans=0;
for(int i=18;~i;i--) if(dep[fa[x][i]]>=dep[y]) ans=max(ans,mx[x][i]),x=fa[x][i];
if(x==y)return ans;
for(int i=18;~i;i--) if(fa[x][i]!=fa[y][i]) ans=max(ans,max(mx[x][i],mx[y][i])),x=fa[x][i],y=fa[y][i];
ans=max(ans,max(mx[x][0],mx[y][0]));
return ans;
}
inline void main(){
read();int Q=read();
for(int i=1;i<=100000;i++) Fa[i]=i,e[i].u=read(),e[i].v=read(),e[i].val=read();
sort(e+1,e+100001);
for(int i=1;i<=100000;i++){
int u=find(e[i].u),v=find(e[i].v);
if(u!=v){
Fa[u]=v;
addedge(e[i].u,e[i].v,e[i].val);
}
}
for(int i=1;i<=50000;i++) if(!dep[i]) dfs(i,0);
while(Q--){
int x=read(),y=read();
if(find(x)!=find(y))printf("%d\n",INF);
else printf("%d\n",solve(x,y));
}
}
}

Point 4

后面的全做完了,回头看一眼 01 串和 012 串,没啥思路,直接看第四个。

观察了一下输入输出,发现输入输出都是回文

什么玩意是回文的?

第一个字符大概是 n,发现第一项是 1,第二项是 n。于是有个聚铑很自然地想到是组合数,然后发现输出也是对应的 n 的一行组合数,于是就切了。

拆了一下数,发现模数是 104857601,一个 NTT 模数

namespace subtask4{
const int maxn=3e5;
int mul[maxn],inv[maxn],n;
inline int C(int n,int m){return 1ll*mul[n]*inv[m]%mod*inv[n-m]%mod;}
inline void pre(){
mul[0]=inv[0]=1;
for(int i=1;i<=n;i++) mul[i]=1ll*mul[i-1]*i%mod;
inv[n]=fpow(mul[n],mod-2);for(int i=n-1;i;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
}
inline void main(){
n=262144;
pre();
printf("%d\n",n);
for(int i=0;i<=n;i++) printf("%d\n",C(n,i));
}
}

PS

后来思考了一下为什么输入把 131072 的一行组合数也全给了,根据二项式定理,输入给出的是 \((x+1)^n\),然后输出的 n 恰好是输入的两倍。所以实际上是让我们算 \((x+1)^{2n}\)。也就是做一遍多项式乘法。

会有人写这玩意吗,还是为了给没看出来是组合数的选手分?

Point 5

刚才切了第四个点的聚铑趁热打铁,瞬间就看出来了是每一项乘上了一个 \((-1)^i\) 。于是又切了。

当然,如果您想写多项式开根也可以。

因为和前面一个点比较像,就放在一起了。

namespace subtask4{
const int maxn=3e5;
int mul[maxn],inv[maxn],n;
inline int C(int n,int m){return 1ll*mul[n]*inv[m]%mod*inv[n-m]%mod;}
inline void pre(){
mul[0]=inv[0]=1;
for(int i=1;i<=n;i++) mul[i]=1ll*mul[i-1]*i%mod;
inv[n]=fpow(mul[n],mod-2);for(int i=n-1;i;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
}
inline void main2(){
n=131072;
pre();
printf("%d\n",n);
for(int i=0;i<=n;i++) printf("%d\n",i&1?mod-C(n,i):C(n,i));
}
}

Point 6

刚才那位聚铑乘胜追击,观察了一下输入输出的项数,发现输出刚好是输入的 \(\frac{1}{3}\) 。

什么东西能减少项数,而且刚好减到 \(\frac{1}{3}\)?

只有聚铑知道答案。他一眼就看出来这似乎是一个多项式三次剩余,然后确实是对的。

然而珂爱似乎有更好的方法,因为这个 \(5e5\) 的数据范围,还是用的多项式快速幂真的太勉强了,加了快读快输开优化才能过。用珂爱的方法或者多项式三次方根或许能更好(更短)地通过此题。

namespace subtask6{
const int maxn=533000<<2,mod=104857601,g=3,gi=104857602/3,n=177147,p=63776689;
struct NTT{
int r[maxn],lim;
inline void getr(int li){
lim=li;
for(int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)*(lim>>1));
}
inline void operator () (int *a,int type) const {
for(int i=0;i<lim;i++) if(i<r[i]) swap(a[i],a[r[i]]);
for(int mid=1;mid<lim;mid<<=1){
int rt=fpow(type==1?g:gi,(mod-1)/(mid<<1));
for(int r=mid<<1,j=0;j<lim;j+=r){
int p=1;
for(int k=0;k<mid;k++,p=1ll*p*rt%mod){
int x=a[j+k],y=1ll*p*a[j+k+mid]%mod;
a[j+k]=(x+y)%mod,a[j+k+mid]=(x-y+mod)%mod;
}
}
}
if(type==-1) for(int p=fpow(lim,mod-2),i=0;i<lim;i++) a[i]=1ll*a[i]*p%mod;
}
}ntt;
void inv(const int *a,int *ans,int n){
if(n==1) return ans[0]=fpow(a[0],mod-2),ans[1]=0,void();
static int res[maxn];
inv(a,ans,n>>1);
int lim=n<<1;
ntt.getr(lim);
for(int i=0;i<n;i++) res[i]=a[i];
for(int i=n;i<lim;i++) res[i]=ans[i]=0;
ntt(res,1),ntt(ans,1);
for(int i=0;i<lim;i++) ans[i]=ans[i]*(2-1ll*ans[i]*res[i]%mod+mod)%mod;
ntt(ans,-1);
for(int i=n;i<lim;i++) ans[i]=0;
}
inline void deri(const int *a,int *ans,int n){for(int i=1;i<n;i++) ans[i-1]=1ll*a[i]*i%mod;ans[n-1]=0;}
inline void inte(const int *a,int *ans,int n){for(int i=1;i<n;i++) ans[i]=1ll*a[i-1]*fpow(i,mod-2)%mod;ans[0]=0;}
inline void ln(const int *a,int *ans,int n){
static int res[maxn];
deri(a,res,n);
inv(a,ans,n);
int lim=n<<1;
ntt.getr(lim);
ntt(res,1),ntt(ans,1);
for(int i=0;i<lim;i++) res[i]=1ll*res[i]*ans[i]%mod,ans[i]=0;
ntt(res,-1);
inte(res,ans,n);
for(int i=0;i<lim;i++) res[i]=0;
}
void exp(const int *a,int *ans,int n){
if(n==1) return ans[0]=1,ans[1]=0,void();
static int res[maxn];
exp(a,ans,n>>1);
ln(ans,res,n);
int lim=n<<1;
ntt.getr(lim);
res[0]=(1+a[0]-res[0]+mod)%mod;
for(int i=1;i<n;i++) res[i]=(a[i]-res[i]+mod)%mod;
ntt(ans,1),ntt(res,1);
for(int i=0;i<lim;i++) ans[i]=1ll*ans[i]*res[i]%mod,res[i]=0;
ntt(ans,-1);
for(int i=n;i<lim;i++) ans[i]=0;
}
inline void fpow(int const *a,int *ans,int k,int n){
static int f[maxn],g[maxn];
for(int i=0;i<n;i++)g[i]=f[i]=ans[i]=0;
int d=0;
while(!a[d]&&d<n) ++d;
int u=::fpow(a[d],mod-2),v=22131490;
for(int i=0;i<n-d;i++)g[i]=1ll*a[i+d]*u%mod;
for(int i=n-d;i<n;i++)g[i]=0;
ln(g,f,n);
for(int i=0;i<n;i++)f[i]=1ll*f[i]*k%mod;
exp(f,ans,n);
d*=k;
for(int i=n-1;i>=d;i--)ans[i]=1ll*ans[i-d]*v%mod;
for(int i=0;i<d;i++)ans[i]=0;
}
int a[maxn],ans[maxn];
inline void main(){
for(int i=0;i<=n;i++) a[i]=read();
fpow(a,ans,::fpow(3,mod-2),1<<18);
out(n);
for(int i=0;i<=n;i++) out(ans[i]);
}
}

Point 2

终于要直面 01 串了。(由第一个点就可以知道我 01 串相关有多菜)

首先,一位聚铑通过观察文件大小发现字符个数恰好是斐波那契数列的第 33 项。

发现除了前三项,后面几项都符合斐波那契串。而一个斐波那契串内确实有这样的规律。

写了一下,发现是对的。

顺便模一下这位聚铑写得真可爱。

namespace subtask2{
string s[2]={"0","1"};
inline void main(){
for(int i=2;i<33;i++) s[i&1]=s[i&1]+s[i&1^1];
puts(s[0].c_str());
}
}

Point 3

这个点是最艰难的点了。

使劲找规律,先发现字符个数是 \(3^{12}+11\) ,然后思考有什么规律。

除去第一个 0,每 12 个数分开,看起来好像是三进制数每次 +1 再 +2 。

往后看了看,什么玩意

经过艰苦的奋斗,最终还是没找出来,于是去膜拜的珂爱的题解。感觉就是个找规律。因为是抄别人的,所以写出来也没啥意思。贴一下别人的代码。

namespace subtask3{
using namespace std;
void main()
{
unordered_set<string>st;
string s="000000000001",output="";
while(1){
output+=s[0];
st.insert(s);
s+=s[0];
s.erase(0,1);
while(st.count(s)&&s!="000000000000"){
++s[11];
for(int i=11;~i&&s[i]=='3';--i){
s[i]='0';
if(i)++s[i-1];
}
}
if(s=="000000000000")break;
}
cout<<0<<output<<"00000000000\n";
}
}

代码

都在上面了。贴一下我的主函数(包括第十个点的特判)和用到的全局函数和变量。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<cmath>
#include<vector>
#include<string>
#include<unordered_set>
using namespace std;
inline int read(){
int w=0,x=0;char c=getchar();
while(!isdigit(c))w|=c=='-',c=getchar();
while(isdigit(c))x=x*10+(c^48),c=getchar();
return w?-x:x;
}
const int mod=104857601;
char O_[999999],*OU=O_,*OV=OU+999991,OS[21],*OT=OS;
#define F fwrite(O_,1,OU-O_,stdout)
#define O(x) (*(OU=(OU==OV?(F,O_):OU))++=(x))
void out(int x){for(;*OT++=x%10+48,x/=10;);for(;OT!=OS;O(*--OT));O(10);}
inline int fpow(int a,int b){int ans=1;for(;b;b>>=1,a=1ll*a*a%mod) if(b&1) ans=1ll*ans*a%mod;return ans;}
int n;
//------------------------------------------------------------
signed main(){
char s[10];
scanf("%s",s);
if(strcmp(s,"Maybe")==0) return puts("Your program should output itself here.\nSounds very difficult, yeah?\nAnyway, good luck!"),0;
n=atoi(s);
if(n==22)subtask1::main();
if(n==33)subtask2::main();
if(n==12)subtask3::main();
if(n==531441)subtask6::main();
if(n==131072)subtask4::main();
if(n==262144)subtask4::main2();
if(n==100000){
int m=read();
if(m==100000) subtask7::main(n,m);
else subtask8::main();
}else if(n==50000) subtask9::main();
return F,0;
}

P5042 丢失的题面的更多相关文章

  1. SCOI 2019 划水记

    (此处不应有目录,爆零的过程得慢慢看) 鸽子来更新游记了 orz UESTC. Day -1 两场傻逼信心考试都没AK. 第一场真的气,一个边界觉得是i - 1,然后不知道为啥改成了i,挂了6个点,然 ...

  2. HEOI 十二省联考退役记

    Day -1 简要的说了些注意事项 一整天都在刷树套树的水题 退役的感觉近了 Day 0 早上收拾好东西去了火车站之后 火车站居然还没有开门 等了半天 我们是从衡水到德州再到秦皇岛 到了德州之后 去车 ...

  3. HEOI2019游记(退役记)

    少了回程铁路相关信息,有空补 AFO 辣鸡蒟蒻ghj1222顺利地退役了 由于没带手机拍照片,本次坐动车不写运转记录,下次去CTS/APIO应该是坐普速车,应该能带手机拍照,应该会写运转记录 Day ...

  4. 关于"丢失的牛"这个题的教学反思

    某天上课讲到这样一个题:丢失的牛1~n,乱序排列,告诉从第二个位置到最后一个位置, 每个位置的前面的数字中比它小的数的个数,求每个位置的数字是多少N<=8000 FormatInput第一行给出 ...

  5. [LeetCode] Missing Number 丢失的数字

    Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one that is missin ...

  6. 递归一题总结(OJ P1117倒牛奶)

    题目:                    农民约翰有三个容量分别是A,B,C升的桶,A,B,C分别是三个从1到20的整数,最初,A和B桶都是空的,而C桶是装满牛奶的.有时,约翰把牛奶从一个桶倒到另 ...

  7. jsp考试的错题

    (选择一项) A: B: C: D: 正确答案是 A,首先,session的出现确实是为了解决HTTP无法保持客户状态的特点:因此A选项正确:用户信息也是客户状态的一部分,所以由A可以看出B的说法就不 ...

  8. jsp前三章测试改错题

      (选择一项) A: B: C: D: 正确答案是 B ,B/S架构并不是C/S架构的替代品,有些程序例如大型的网络游戏一般使用的是C/S架构. (选择多项) A: B: C: D: 正确答案是 A ...

  9. 玉伯的一道课后题题解(关于 IEEE 754 双精度浮点型精度损失)

    前文 的最后给出了玉伯的一道课后题,今天我们来讲讲这题的思路. 题目是这样的: Number.MAX_VALUE + 1 == Number.MAX_VALUE; Number.MAX_VALUE + ...

随机推荐

  1. 负载均衡算法: 简单轮询算法, 平滑加权轮询, 一致性hash算法, 随机轮询, 加权随机轮询, 最小活跃数算法(基于dubbo) java代码实现

    直接上干活 /** * @version 1.0.0 * @@menu <p> * @date 2020/11/17 16:28 */ public class LoadBlance { ...

  2. 给小米路由R1D增加WebDAV服务

    我的R1D是14年买的,原装的硬盘已经不能用了,换了一块从笔记本上退役下来的500G硬盘后继续愉快的使用了-- 当初买这款路由器的原因之一是看中了它的内置硬盘,可以用来备份手机相册.存储智能摄像机录像 ...

  3. 使用fiddler工具模拟弱网环境

    1.使用CTRL+R快捷键调出设置窗口 2.CTRL+F搜索"if (m_SimulateModem)",如下图   3.设置 // 这里修改每KB上传时候的延迟速度,以ms为单位 ...

  4. js正则中文

    hi,大家好 今天跟小伙伴们浅谈以下如何用正则表示中文以及如何去运用.众所周知中文在计算机中是不能进行存储的.那我们是以什么办法让我们和计算机进行更好的沟通呢?常用的几种中文编码格式utf-8编码ut ...

  5. 一文带你了解.Net互斥锁

    本文主要讲解.Net基于Threading.Mutex实现互斥锁 基础互斥锁实现 基础概念:和自旋锁一样,操作系统提供的互斥锁内部有一个数值表示锁是否已经被获取,不同的是当获取锁失败的时候,它不会反复 ...

  6. Pytorch Dataset和Dataloader 学习笔记(二)

    Pytorch Dataset & Dataloader Pytorch框架下的工具包中,提供了数据处理的两个重要接口,Dataset 和 Dataloader,能够方便的使用和加载自己的数据 ...

  7. centos 7 增加一块硬盘的步骤

    1) 增加一块硬盘 1G [到设置中添加一块硬盘即可] 2) 重启一下服务器 lsblk -f 查看硬盘 3) 给 sdb 硬盘分区 fdisk /dev/sdb n,p,enter,enter,w ...

  8. Jenkins 构建自动化 .NET Core 发布镜像

    Jenkins 构建自动化 .NET Core 发布镜像 导读 在本章中,将介绍如何在 Linux 下使用 Docker 部署.启动 Jenkins,编写脚本,自动化构建 .NET Core 应用,最 ...

  9. Netty 框架学习 —— 基于 Netty 的 HTTP/HTTPS 应用程序

    通过 SSL/TLS 保护应用程序 SSL 和 TLS 安全协议层叠在其他协议之上,用以实现数据安全.为了支持 SSL/TLS,Java 提供了 javax.net.ssl 包,它的 SSLConte ...

  10. ClickHouse源码笔记6:探究列式存储系统的排序

    分析完成了聚合以及向量化过滤,向量化的函数计算之后.本篇,笔者将分析数据库的一个重要算子:排序.让我们从源码的角度来剖析ClickHouse作为列式存储系统是如何实现排序的. 本系列文章的源码分析基于 ...