zoukankan      html  css  js  c++  java
  • P5042 丢失的题面

    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
    ",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
    ",INF);
    			else printf("%d
    ",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);
    		for(int i=0;i<=n;i++) printf("%d
    ",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);
    		for(int i=0;i<=n;i++) printf("%d
    ",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
    ";
    	}
    }
    

    代码

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

    #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.
    Sounds very difficult, yeah?
    Anyway, 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;
    }
    
  • 相关阅读:
    Java的HashMap
    为什么 char c = 'A';c += 32; 结果输出的是 'a'?
    java中整数的常量优化机制
    IDEA2019版中文汉化包
    asp.net项目协作开发时,常常要忽略版本控制的目录
    SQLServer同步数据到ElasticSearch
    为什么不建议在微信小程序的TarBar上检查统一登录后跳转页面
    nginx的热备方式
    HTTP 和FTP 状态信息总结(留着自己用)
    Web Api 简介
  • 原文地址:https://www.cnblogs.com/BrotherHood/p/14192148.html
Copyright © 2011-2022 走看看