把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CodeChef】August Challenge 2019 Div2 解题报告

点此进入比赛

\(T1\):Football(点此看题面

大致题意:\(max(20a_i-10b_i,0)\)

送分题不解释。

#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 150
#define Gmax(x,y) (x<(y)&&(x=(y)))
using namespace std;
int n,a[N+5],b[N+5];
int main()
{
	RI Tt,i,x,y,ans;scanf("%d",&Tt);W(Tt--)
	{
		for(scanf("%d",&n),ans=0,i=1;i<=n;++i) scanf("%d",a+i);
		for(i=1;i<=n;++i) scanf("%d",b+i),Gmax(ans,20*a[i]-10*b[i]);//统计答案
		printf("%d\n",ans);
	}return 0;
}

\(T2\):Distribute Apples(点此看题面

大致题意: 有两种分苹果方法,一种把\(n\)个苹果平均分到\(k\)个盒子中,一种每次在一个盒子中放\(k\)个苹果放\(\frac nk\)次,判断两种放法结果是否一样。

考虑苹果总数是一样的,因此只要第二种方法能做到平均分配,结果就是一样的。

而第二种方法做到平均分配,就说明\(k|\frac nk\)

判一下即可。

#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 150
#define Gmax(x,y) (x<(y)&&(x=(y)))
using namespace std;
long long n,k;
int main()
{
	RI Tt,i,x,y,ans;scanf("%d",&Tt);W(Tt--)
		scanf("%lld%lld",&n,&k),puts((n%k||(n/k)%k)?"YES":"NO");//判断k|(n/k)
	return 0;
}

\(T3\):Dilemma (点此看题面

大致题意: 给你一个\(01\)序列,每次能把一个为\(1\)的位置变成\(-1\),将其相邻的\(0\)变成\(1\)\(1\)变成\(0\)。问是否能把所有位置变成\(-1\)

结论题?

首先,可以发现,如果把若干个连续\(0\)换成一个\(0\),答案不变,如\(101\)\(1001\)

其次,可以发现,若只有长度为偶数的连续\(1\)的序列,是不可以的,如\(1111\)\(01101111110\)。但只要有一个长度为奇数的连续\(1\)的序列,就可以了,如\(111011\)。也就是说,除去长度为偶数的连续\(1\)的序列,答案不变。

也就是说,我们只需考虑长度为奇数的连续\(1\)的序列。

然后画图找找规律,就可以发现当有奇数个长度为奇数的连续\(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 100000
#define Gmax(x,y) (x<(y)&&(x=(y)))
using namespace std;
int n;char s[N+5];
int main()
{
	RI Tt,i,t=0,res=0;scanf("%d",&Tt);W(Tt--)
	{
		for(scanf("%s",s),n=strlen(s),t=res=i=0;i^n;++i)
			s[i]&1?(t^=1):(res^=t,t=0);puts(res^t?"WIN":"LOSE");//t统计当前连续1序列的奇偶性,res统计长度为奇数的连续1的序列的个数的奇偶性
	}return 0;
}

\(T4\):Zombie and the Caves(点此看题面

大致题意: 给你个数组\(C\)以及一个空序列,对于每个\(i\),把序列中第\(i-C_i\sim i+C_i\)个位置都加上\(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 100000
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
int n,s[N+5],p[N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define tn (x<<3)+(x<<1)
		#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=tn+(c&15),D);}
		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}F;
int main()
{
	RI Tt,i,x,f;F.read(Tt);W(Tt--)
	{
		for(F.read(n),i=1;i<=n;++i) s[i]=p[i]=0;//清空
		for(i=1;i<=n;++i) F.read(x),++s[max(i-x,1)],--s[min(i+x,n)+1];//差分
		for(i=1;i<=n;++i) (s[i]+=s[i-1])<=n&&++p[s[i]];//求出序列并存入桶中
		for(f=i=1;i<=n;++i) F.read(x),(x>n||!p[x]--)&&(f=0);puts(f?"YES":"NO");//判断是否可以一一匹配
	}return 0;
}

\(T5\):Guddu and his Mother(点此看题面

大致题意: 给你一个序列,求有多少组\((i,j,k)\)满足\(xor_{x=i}^{j-1}a_x=xor_{x=j}^ka_x\)

首先容易发现,对于一组\((i,k)\),若其满足\(xor_{x=i}^ka_x=0\),则对于满足\(i<j\le k\)的任意一组\((i,j,k)\)都是一组合法解,即有\(k-i\)组合法解。

若我们记\(s_i=xor_{x=1}^ia_x\),就相当于\(s_{i-1}\ xor\ s_k=0\),即\(s_{i-1}=s_k\)时,有\(k-i\)组合法解。

也就是当\(s_i=s_k\)时,有\(k-i-1=(k-1)-i\)组合法解。

所以我们开两个\(map\)\(p_x\)表示之前所有满足\(s_i=x\)\(i\)的和,\(g_x\)表示之前满足\(s_i=x\)\(i\)的个数。

那么我们枚举\(i\),就可以更新\(ans\)加上\(g_{s_i}(i-1)-p_{s_i}\)

#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 LL long long
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
int n,s[N+5];map<int,LL> p,g;
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define tn (x<<3)+(x<<1)
		#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=tn+(c&15),D);}
		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}F;
int main()
{
	RI Tt,i,x;LL ans;F.read(Tt);W(Tt--)
	{
		ans=0,p.clear(),g.clear(),p[0]=0,g[0]=1;//清空
		for(F.read(n),i=1;i<=n;++i) F.read(x),//读入
			s[i]=s[i-1]^x,ans+=1LL*g[s[i]]*(i-1)-p[s[i]],p[s[i]]+=i,++g[s[i]];//统计前缀和,更新答案,更新map
		printf("%lld\n",ans);//输出答案
	}return 0;
}

\(T6\):Encoding(点此看题面

大致题意:\(F(x)\)\(x\)十进制数位中每段连续相同数字只保留最高位、其余位替换为\(0\)后的值,求\(\sum_{i=l}^rF(i)\)

显然数位\(DP\)

我们设\(f_{x,lst}\)\(g_{x,lst}\)分别表示右数第\(x\)位、上一个数为\(lst\)时的答案和情况数。

考虑记忆化搜索。

对于答案,转移时先将后继状态的\(f\)求和,然后若枚举到这一位的值\(y\)不等于\(lst\),就将答案加上后继状态的\(g\)\(y*10^x\)

对于情况数,直接将后继状态的\(g\)求和即可。

#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 X 1000000007
#define Inc(x,y) ((x+=(y))>=X&&(x-=X))
using namespace std;
int nl,nr;char L[N+5],R[N+5];
class DigitalDP
{
	private:
		#define Pr pair<int,int>
		#define mp make_pair
		#define fir first
		#define sec second
		int v[N+5],tn[N+5],f[N+5][10],g[N+5][10];
		I Pr DP(CI x,CI lst,CI p)//数位DP
		{
			RI i,lim=p?v[x]:9;Pr t,k=mp(0,0);
			if(!x) return mp(0,1);if(!p&&~f[x][lst]) return mp(f[x][lst],g[x][lst]);//若求过答案,直接返回
			for(i=0;i<=lim;++i) t=DP(x-1,i,p&&(i==lim)),//枚举这一位填的数,处理后继状态
				Inc(k.fir,t.fir),Inc(k.sec,t.sec),i^lst&&(k.fir=(1LL*tn[x]*i%X*t.sec+k.fir)%X);//统计答案和情况数
			return f[x][lst]=k.fir,g[x][lst]=k.sec,k;//记忆化,返回答案
		}
	public:
		I DigitalDP() {RI i;for(tn[1]=1,i=2;i<=N;++i) tn[i]=10LL*tn[i-1]%X;}//初始化10的幂
		I int GetAns(CI x,char *s)
		{
			RI i,ans=0;Pr t;for(i=x;i;--i) v[i]=s[x-i]&15;memset(f,-1,sizeof(f));//清空
			for(i=0;i<=v[x];++i) t=DP(x-1,i,i==v[x]),ans=(1LL*tn[x]*i%X*t.sec+t.fir+ans)%X;//枚举最高位,这个写法有点蠢啊
			return ans;
		}
}D;
int main()
{
	RI Tt,p;scanf("%d",&Tt);W(Tt--)
	{
		scanf("%d%s%d%s",&nl,L,&nr,R);p=nl-1;W(L[p]=='0') L[p--]='9';--L[p];//将L-1差分
		printf("%d\n",(D.GetAns(nr,R)-D.GetAns(nl,L)+X)%X);//求答案
	}return 0;
}

\(T7\):Chef and Gordon Ramsay(点此看题面

大致题意: 求有多少对\((x,y,z)\)满足\(y\)\(x\)\(z\)树上最短路径上,且满足给定的相对大小。

考虑一下线段树合并+大分类讨论。

我们先线段树合并,用值域线段树存下每个子树内的所有值。

然后,枚举\(y\),并对\(y\)从大到小的相对排名,以及路径完全在子树内还是部分在子树外,分类讨论:

  • \(y\)相对排名为\(1\),求出子树内大于\(y\)的数的个数乘子树外大于\(y\)的数的个数(子树外的可以差分),并枚举子节点加上该儿子的子树内大于\(y\)的数的个数乘其他儿子子树内大于\(y\)的数的个数(其他儿子的可以差分)除以\(2\)
  • \(y\)相对排名为\(2\),求出子树内大于\(y\)的数的个数乘子树外小于\(y\)的数的个数加上求出子树内小于\(y\)的数的个数乘子树外大于\(y\)的数的个数,并枚举子节点加上该儿子的子树内小于\(y\)的数的个数乘其他儿子子树内大于\(y\)的数的个数。
  • \(y\)相对排名为\(3\),与相对排名为\(1\)类似,略。

注意卡常,有些用的很多次的值不要每次都到线段树上去查询,可以直接存下来。

#pragma GCC optimize(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
#define N 100000
#define LN 20
#define LL long long
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,ee,lnk[N+5],Sz[N+5],p[4];LL ans;struct edge {int to,nxt;}e[N<<1];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define tn (x<<3)+(x<<1)
		#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=tn+(c&15),D);}
		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}F;
template<int SZ,int PS> class SegmentTree//线段树
{
	private:
		#define LT l,mid,O[rt].S[0]
		#define RT mid+1,r,O[rt].S[1]
		#define PU(x) (O[x].V=O[O[x].S[0]].V+O[O[x].S[1]].V)
		int n,Nt,Rt[SZ+5];struct node {int V,S[2];}O[PS+5];
		I void Ins(CI v,CI l,CI r,int& rt)//单点修改
		{
			if(!rt&&(rt=++Nt,O[rt].S[0]=O[rt].S[1]=0),l==r) return (void)(O[rt].V=1);
			RI mid=l+r>>1;v<=mid?Ins(v,LT):Ins(v,RT),PU(rt);
		}
		I int Qry(CI x,CI y,CI l,CI r,CI rt)//区间求和
		{
			if(!rt) return 0;if(x<=l&&r<=y) return O[rt].V;RI mid=l+r>>1;
			return (x<=mid?Qry(x,y,LT):0)+(y>mid?Qry(x,y,RT):0);
		}
		I int Merge(CI l,CI r,CI x,CI y)//线段树合并
		{
			if(!x||!y) return x+y;RI rt=++Nt;if(l==r) return (O[rt].V=O[x].V+O[y].V);RI mid=l+r>>1;
			O[rt].S[0]=Merge(l,mid,O[x].S[0],O[y].S[0]),O[rt].S[1]=Merge(mid+1,r,O[x].S[1],O[y].S[1]);
			return PU(rt),rt;
		}
	public:
		I void Init(CI _n) {Nt=0,n=_n;for(RI i=1;i<=n;++i) Rt[i]=0;}
		I void Ins(CI id,CI v) {Ins(v,1,n,Rt[id]);}
		I int Qry(CI id,CI l,CI r) {return Qry(l,r,1,n,Rt[id]);}
		I void Merge(CI x,CI y) {Rt[x]=Merge(1,n,Rt[x],Rt[y]);}
};SegmentTree<N,N*LN<<1> S;
I void dfs(CI x,CI lst=0)
{
	RI i;for(Sz[x]=1,S.Ins(x,x),i=lnk[x];i;i=e[i].nxt)//处理子树,合并子树线段树
		e[i].to^lst&&(dfs(e[i].to,x),Sz[x]+=Sz[e[i].to],S.Merge(x,e[i].to),0);
	LL res=0;RI t,t1=S.Qry(x,1,x-1),t2=Sz[x]-t1-1;
	if(p[2]==1)//若y相对排名为1
	{
		ans+=1LL*(n-x-t2)*t2;//部分在子树外
		for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&//完全在子树内
			(t=S.Qry(e[i].to,x+1,n),res+=1LL*(t2-t)*t);
	}
	else if(p[2]==2)//若y相对排名为2
	{
		ans+=1LL*(n-x-t2)*t1+1LL*(x-1-t1)*t2;//部分在子树外
		for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&//完全在子树内
			(t=S.Qry(e[i].to,x+1,n),res+=1LL*(t2-t)*(Sz[e[i].to]-t));
	}
	else//若y相对排名为3
	{
		ans+=1LL*(x-1-t1)*t1;//部分在子树外
		for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^lst&&//完全在子树内
			(t=S.Qry(e[i].to,1,x-1),res+=1LL*(t1-t)*t);
	}ans+=res>>(p[2]!=2);
}
int main()
{
	RI Tt,i,x,y;F.read(Tt);W(Tt--)
	{
		for(F.read(n),S.Init(n),ee=0,i=1;i<=n;++i) lnk[i]=0;//清空
		for(i=1;i<=3;++i) F.read(p[i]);for(i=1;i^n;++i) F.read(x,y),add(x,y),add(y,x);//读入建边
		ans=0,dfs(1),printf("%lld\n",ans);//输出答案
	}return 0;
}
posted @ 2019-08-13 12:42  TheLostWeak  阅读(255)  评论(0编辑  收藏  举报