2020“游族杯”网络挑战赛划水记(剩下的题目暂时先坑着吧)

前言

绝不放过任何一个可以水的机会。

和去年相比毫无长进,甚至被吊打系列:2019“游族杯”上海游记。(虽说去年也挺弱的)

emmm 无话可说了已经。。。

那么水的(T1),我死活做不出来,整场比赛花了一半时间去想,第二天才得知是英文题意理解错了。。。

败在英语上,以后还是好好学习英语吧,碰上英文题就当阅读理解来做。

我这么弱,又浪费了大量时间,其他题目虽然有些想法,最后自然也来不及写了。

感觉要不是这(T1),我说不定还能多做出来一两道题吧。

谨以此篇,作为我菜的证明。

(A):Amateur Chess Players(点此看题面

还能说些什么呢?

显然,众所周知,一场(ACM)比赛(T1)肯定是道(SB)题。

因此,看完题目我第一反应就是猜结论(n>m)时先手赢,(nle m)时后手赢。

然后我就无比愉悦地把自己(Hack)掉了。

为什么呢?因为我看错题目了!

我以为一个人每次可以把至少一个自己的棋子以及共线的对方棋子一起移走,后来第二天才知道一个人每次只能拿走一条线上自己的棋子!

那么一个显然的贪心每人每次只会移走一个棋子,然后只要比下谁的棋子多就好了。。。

可怜一场比赛(4)个小时,我前前后后想这道题就累计花下了(2)个小时左右,心态直接崩了,只能默默接受被吊打的命运。

说实话我也不知道按我理解的题意能不能做,主要是我知道这是水题所以一直以水题的标准去思考,说不定可能还真有什么高端做法能搞这种问题呢。(反正我不会就对了)

算了,菜就是菜,我不应为自己的菜找借口,即使是狡辩也无法改变我菜的事实。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
using namespace std;
int n,m;string s;
int main()
{
	RI i;for(scanf("%d",&n),i=1;i<=n;++i) cin>>s;scanf("%d",&m);//读入
	return puts(n>m?"Cuber QQ":"Quber CC"),0;//输出
}

(B):Binary String(点此看题面

感觉这道交互题难度不大吧。

之前嫌交互烦,先跳了。比赛最后半个多小时开始看题,一开始看错题面(所以说我真的不擅长做英文题。。。),以为是至少长度为(lfloorfrac n2 floor+1),于是就开始写,写完后提交之前看了眼样例发现不太对劲,然后就悲剧了。。。

最终,我成功口胡出了正解,然而,并没能写完。

唉,要不是(T1)。。。算了,我已经无力吐槽自己了。

考虑这个(1023),它恰好等于(2^{10}-1),使我最初看到这个限制时以为和二进制之类的东西有关,一脸茫然。

但实际上仔细一想,就会发现(nle1000),所以(1023=1000+23),大概就相当于一个(n)加上两个(logn)(讲道理最终好像只用一个(n)和一个(logn),所以我确信这个(1023)绝对是放这里误导人的)。

容易发现在(n)(01)中,(0)(1)中某一个数的出现次数肯定小于等于(lfloorfrac n2 floor),不妨设这个数为(0)。而我们可以通过二分来求出(0)的个数(cnt)

则有一个显然的想法,我们去枚举每一个(0),然后求它左边有多少个(1)

由于从左到右枚举(0)的过程中(1)的个数肯定不会变少,所以我们可以用一个变量(t)存储当前(1)的个数,每次只要尝试验证即可。

考虑如何验证第(i)(0)左边是不是有(t)(1)

如果我们询问(t)(1)以及(cnt-i+1)(0),如果此时返回(0),就说明不存在(t)(1),而由于我们是依次枚举(t)的,所以(1)的个数就是(t-1)

但是,如果(t+cnt-i+1>lfloorfrac n2 floor+1),不满足询问的限制怎么办?

没关系,如果左边不满足限制,那么我们询问(i)(0)以及(n-cnt-t+1)(1),如果此时返回(1),就说明右边存在(n-cnt-t+1)(1),即左边不存在(t)(1),所以(1)的个数就是(t-1)

而考虑此时询问的长度是(i+n-cnt-t+1=n-(t+cnt-i)+1),显然在上面的式子成立时,该长度一定小于等于(lfloorfrac n2 floor+1)

于是这道题就做完了,具体实现详见代码。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000
#define pc putchar
#define Qry() (pc('
'),fflush(stdout),scanf("%d",&res),res)
using namespace std;
int n,lim,cnt,res,p[N+5];
int main()
{
	RI i,j,Base;scanf("%d",&n),lim=(n>>1)+1;
	for(pc('?'),pc(' '),i=1;i<=(n+1>>1);++i) pc('0');Base=Qry();//判断出现次数小于等于一半的数是谁(设为“0”)
	RI l=0,r=n>>1,mid;W(l<r)//二分求出“0”的出现次数
	{
		pc('?'),pc(' '),mid=l+r+1>>1;
		for(i=1;i<=mid;++i) pc(Base+48);Qry()?l=mid:r=mid-1;
	}cnt=l;
	RI t=1;for(i=1;i<=cnt;p[i++]=t-1) W(t<=n-cnt)//枚举“0”,求出左边“1”的个数
	{
		if(pc('?'),pc(' '),t+cnt-i+1<=lim)//在左边测试
		{
			for(j=1;j<=t;++j) pc((Base^1)+48);for(j=1;j<=cnt-i+1;++j) pc(Base+48);
			if(!Qry()) break;//如果不存在,说明不合法了
		}
		else//在右边测试
		{
			for(j=1;j<=i;++j) pc(Base+48);for(j=1;j<=n-cnt-t+1;++j) pc((Base^1)+48);
			if(Qry()) break;//如果存在,说明不合法了
		}++t;//将t加上1
	}
	for(pc('!'),pc(' '),t=0,i=1;i<=cnt;++i) {W(t<p[i]) pc((Base^1)+48),++t;pc(Base+48);}//输出答案
	W(cnt+t<n) pc((Base^1)+48),++t;return pc('
'),fflush(stdout),0;
}

(C):Coronavirus Battle(点此看题面

没太看懂题解里在讲啥,反正对于这种高维偏序的题目,我写的是(KD-Tree)。。。

后来听(jxc)大佬说,似乎一个线段树就能解决,看来我还是太菜。

考虑我们先按第一维排个序(第一维相同比第二维,第二维还相同比第三维,反正就让小的一定在大的前面),则此时第一维显然满足了限制,因此我们对于二三两维建一个二维的(KD-Tree)

那么,一个点的答案,就是两维坐标都小于它的点的答案的最大值加(1),显然用(KD-Tree)可以很套路地优秀地实现这一询问过程。(具体实现详见代码)

然后,我们把这个答案修改到(KD-Tree)上,显然单点修改最多也就是(O(logn))的。

于是这道题就如此轻松愉快地解决了。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define ull unsigned long long
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,D,ans;ull k1,k2,k3,k4;struct Data {ull x,y,z;int v,id;}s[N+5];
struct Point
{
	ull x[2];I ull& operator [] (CI d) {return x[d];}
	I bool operator < (Con Point& o) Con {return x[D]^o.x[D]?x[D]<o.x[D]:x[D^1]<o.x[D^1];}
}p[N+5];
I bool cmp(Con Data& x,Con Data& y) {return x.x^y.x?x.x<y.x:(x.y^y.y?x.y<y.y:x.z<y.z);}
I bool cmpid(Con Data& x,Con Data& y) {return x.id<y.id;}
I ull R() {k3=k1,k4=k2,k1=k4,k3^=k3<<23,k2=k3^k4^(k3>>17)^(k4>>26);return k2+k4;}
class FastWrite
{
	private:
		#define FS 100000
		#define pc(c) (C==E&&(clear(),0),*C++=c)
		int T;char *C,*E,FO[FS],S[FS];
	public:
		I FastWrite() {C=FO,E=FO+FS;}
		Tp I void write(Ty x,char y) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc(y);}
		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
class KDTree//KD-Tree
{
	private:
		int rt,Nt,ans;struct node {int F,G,S[2];Point V,Mx,Mn;}O[N+5];
		I void PU(CI x)//上传信息
		{
			RI i;for(i=0;i^2;++i) O[x].Mx[i]=O[x].Mn[i]=O[x].V[i],
				O[x].S[0]&&(Gmax(O[x].Mx[i],O[O[x].S[0]].Mx[i]),Gmin(O[x].Mn[i],O[O[x].S[0]].Mn[i])),
				O[x].S[1]&&(Gmax(O[x].Mx[i],O[O[x].S[1]].Mx[i]),Gmin(O[x].Mn[i],O[O[x].S[1]].Mn[i]));
			O[x].G=max(O[x].F,max(O[O[x].S[0]].G,O[O[x].S[1]].G));
		}
		I void Build(CI l,CI r,int& rt,Point *P,CI d=0)//建树
		{
			int mid=l+r>>1;D=d,sort(P+l,P+r+1),O[rt=++Nt].V=P[mid],O[rt].F=-1,
			l<mid?(Build(l,mid-1,O[rt].S[0],P,d^1),0):(O[rt].S[0]=0),
			r>mid?(Build(mid+1,r,O[rt].S[1],P,d^1),0):(O[rt].S[1]=0),PU(rt);
		}
		I void Upt(CI rt,Con ull& x,Con ull& y,CI v,CI d=0)//单点修改
		{
			if(O[rt].V.x[0]==x&&O[rt].V.x[1]==y) return (void)(O[rt].F=v,PU(rt));//找到对应点直接修改
			D=d,Upt(O[rt].S[O[rt].V<(Point){x,y}],x,y,v,d^1),PU(rt);//到子树中去修改
		}
		I void Qry(CI rt,Con ull& x,Con ull& y)//询问最大值
		{
			if(!rt||O[rt].G<=ans||O[rt].Mn[0]>x||O[rt].Mn[1]>y) return;//如果最大值不可能造成贡献,或这些点肯定不在范围内,直接return
			if(O[rt].Mx[0]<=x&&O[rt].Mx[1]<=y) return (void)(ans=O[rt].G);//如果全部满足,更新答案
			O[rt].V[0]<=x&&O[rt].V[1]<=y&&Gmax(ans,O[rt].F);//计算当前点贡献
			RI d=O[O[rt].S[1]].G>=O[O[rt].S[0]].G;Qry(O[rt].S[d],x,y),Qry(O[rt].S[d^1],x,y);//优先处理可能带来更优答案的子节点
		}
	public:
		I void Build(Point *P) {O[0].G=-1,Build(1,n,rt,P);}
		I void Upt(Con ull& x,Con ull& y,CI v) {Upt(rt,x,y,v);}
		I int Qry(Con ull& x,Con ull& y) {return ans=-1,Qry(rt,x,y),ans;}
}K;
int main()
{
	RI i;scanf("%d%llu%llu",&n,&k1,&k2);
	for(i=1;i<=n;++i) s[i].id=i,s[i].x=R(),s[i].y=R(),s[i].z=R();//生成数据
	for(sort(s+1,s+n+1,cmp),i=1;i<=n;++i) p[i].x[0]=s[i].y,p[i].x[1]=s[i].z;K.Build(p);//排序,建树
	for(i=1;i<=n;++i)//枚举点
	{
		(s[i].v=K.Qry(s[i].y,s[i].z)+1)>ans&&(ans=s[i].v),K.Upt(s[i].y,s[i].z,s[i].v);//询问答案,并修改
		W(i<=n&&s[i].x==s[i+1].x&&s[i].y==s[i+1].y&&s[i].z==s[i+1].z) s[i+1].v=s[i].v,++i;
		//对于重合的点不再处理,否则会出锅(虽说这么大范围的随机数据几乎不可能有重合)
	}
	for(F.write(ans+1,'
'),sort(s+1,s+n+1,cmpid),i=1;i<=n;++i) F.write(s[i].v," 
"[i==n]);//输出答案
	return F.clear(),0;
}

(D):Decay of Signals(先坑着)

(E):Even Degree(继续坑着)

(F):Find / -type f -or -type d(点此看题面

显然就是个字典树,只不过有些细节问题。

写完后发现似乎先按长度排个序可以省去不少细节问题,不过写都写完也就不管了。。。

考虑直接按题目给出的顺序做,主要的麻烦就是要考虑两个成祖先关系的字符串之间的先后问题。

我们在字典树上插入一个字符串过程中,如果发现某个目录"/"对应的节点造成过贡献(祖先在前,后代在后),我们就减去这一贡献。

然后插入完之后,如果这一字符串是之前某个字符串的祖先目录(祖先在后,后代在前),我们就不计算它的贡献。

这道题总体上来说还是挺水的吧。虽然我智障开小数组RE了一发,然后脑残写错一个细节WA了一发。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define swap(x,y) (x^=y^=x^=y)
using namespace std;
int n,ans;string s;
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define D isdigit(c=tc())
		char c,*A,*B,FI[FS];
	public:
		I FastIO() {A=B=FI;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
		I void reads(string& x) {x="";W(isspace(c=tc()));W(x+=c,!isspace(c=tc())&&~c);}
}F;
class Trie//字典树
{
	private:
		#define SZ 1000000
		int Nt;struct node {int V,K,S[30];}O[SZ+5];
	public:
		I node& operator [] (CI x) {return O[x];}
		I int Ins(Con string& s,CI l)
		{
			#define P(x) (x=='/'?27:(x=='.'?0:(x&31)))//把字符转化成数字
			RI i,x=1,t;for(i=0;i^l;++i)
				s[i]=='/'&&(O[x].K&&(O[x].K=0,--ans),O[x].V=1),//对于一个目录,若之前造成过贡献则减去,并打上目录标记
				(!O[x].S[t=P(s[i])]&&(O[x].S[t]=++Nt),x=O[x].S[t]);//转移到子节点
			return !O[x].V?x:0;//如果该点是目录,就不计算贡献
		}
}T;
int main()
{
	RI i,l,t;for(F.read(n),i=1;i<=n;++i) F.reads(s),(t=T.Ins(s,l=s.length()))&&
		l>=4&&s[l-4]=='.'&&s[l-3]=='e'&&s[l-2]=='o'&&s[l-1]=='j'&&(++ans,T[t].K=1);//如果造成贡献,打上贡献标记
	return printf("%d",ans),0;//输出答案
}

(G):Geralt of Rivia(依然坑着)

(H):Heat Pipes(忙于挖坑)

(I):Idiotic Suffix Array(点此看题面

送分题。

考虑一个后缀比另一个后缀小,首先要比较的自然是第一个字符。

那么现在要让第一个后缀是第(k)小的后缀,我们只要让第一个位置为"b",另选(k-1)个位置为"a",其余位置皆为"c",显然就好了。。。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
using namespace std;
int n,k;char s[N+5];
int main()
{
	scanf("%d%d",&n,&k);s[1]='b';for(RI i=2;i<=k;++i) s[i]='a';//没啥好说的吧
	for(RI i=k+1;i<=n;++i) s[i]='c';return puts(s+1),0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/youzu2020.html