Codechef 一眼题题解合集

文章目录

    • Sereja and Equality
    • Division Game
    • Reach Equilibrium
    • Chef and Soccer
    • Partition into Permutations
    • Travelling Saleschef
    • Kira Loves Palindromes
    • Playing with Numbers
    • Intersecting Paths
    • Sum and GCD
    • Chef and Ingredients

Sereja and Equality

简单组合数学+无脑前缀和优化dp计数。
f i , j f_{i,j} fi,j表示 i i i个数中逆序对数 ≤ j \le j j的排列数
略卡常。。。
早年写过的代码:

#include
#define N 505
#define mod 1000000007
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int C[N][N],f[N][N*N],T,n,k,ans,fac[N];
inline void init(){
	fac[0]=1;
	for(int i=1;i<=500;++i)fac[i]=1ll*fac[i-1]*i%mod;
	for(int i=1;i<=500;++i)C[i][0]=C[i][i]=1,C[i][1]=i;
	for(int i=2;i<=500;++i)for(int j=1;j<=i;++j){
		C[i][j]=C[i-1][j]+C[i-1][j-1];
		if(C[i][j]>=mod)C[i][j]-=mod;
	}
	int sum=0;
	f[1][0]=1;
	for(int i=2;i<=500;++i){
		sum=0;
		for(int j=0;j<=(i-1)*i/2;++j){
			sum+=f[i-1][j];
			if(sum>=mod)sum-=mod;
			f[i][j]=sum;
			if(j+1-i>=0)sum=(sum-f[i-1][j+1-i]+mod)%mod;
		}
	}
	for(int i=2;i<=500;++i)for(int j=1;j<=(i-1)*i/2;++j){
		f[i][j]+=f[i][j-1];
		if(f[i][j]>=mod)f[i][j]-=mod;
	}
}
int main(){
	init(),T=read();
	while(T--){
		n=read(),k=read(),ans=0;
		for(int i=1;i<=n;++i){
			int tmp=1ll*C[n][i]*C[n][i]%mod*f[i][min(i*(i-1)/2,k)]%mod*fac[n-i]%mod*fac[n-i]%mod*(n-i+1)%mod;
			ans+=tmp;
			if(ans>=mod)ans-=mod;
		}
		printf("%d\n",ans);
	}
	return 0;
}

Division Game

传送门
不知道算不算博弈论,手玩一下推 % 4 = 0 , 1 , 2 , 3 \%4=0,1,2,3 %4=0,1,2,3的堆数的奇偶性对应的答案即可。
代码:

#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const int N=1e6+5;
int cnt[4],n;
int main(){
	for(ri tt=read();tt;--tt){
		n=read();
		cnt[0]=cnt[1]=cnt[2]=cnt[3]=0;
		for(ri i=1;i<=n;++i)cnt[read()%4]^=1;
		int ans=cnt[0]*8+cnt[1]*4+cnt[2]*2+cnt[3];
		if(ans==8)puts("Avnish");
		else if(!ans)puts("Avnish");
		else puts("Bishal");
	}
	return 0;
}

Reach Equilibrium

传送门
首先要首推一个结论:模长最长的向量长度不超过总长的一半。
然后从低维向高维推找规律即可。
然后官方题解给出了证明
代码:

#include
#define ll long long
#define mod 1000000007
using namespace std;
inline ll ksm(ll x,ll p){
	ll ret=1;
	while(p){
		if(p&1)ret=ret*x%mod;
		x=x*x%mod;
		p>>=1;
	}
	return ret;
}
int main(){
	ll n,k;
	scanf("%lld%lld",&n,&k);
	printf("%lld",(ksm(2,n-1)-n+mod)%mod*ksm(2,(n-1)*(mod-2))%mod);
	return 0;
}

Chef and Soccer

传送门
这题我用了一个挺复杂的 d p dp dp,貌似可以更简单???然而因为水过去了就懒得看其它做法了
f i , 0 / 1 / 2 , 0 / 1 , 0 / 1 f_{i,0/1/2,0/1,0/1} fi,0/1/2,0/1,0/1表示走到 i i i点,这一步向右走 0 / 1 / 2 0/1/2 0/1/2格( 0 0 0格表示在这里结束),第 i + 1 i+1 i+1个格子是否走过,第 i + 2 i+2 i+2个格子是否走过的档案数。
g i , 0 / 1 / 2 g_{i,0/1/2} gi,0/1/2表示走到 i i i点,从这一步开始一直向左走且这一步走了 0 / 1 / 2 0/1/2 0/1/2步的方案数。
然后简单讨论一下即可。
代码:

#include
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
typedef long long ll;
const int N=1e5+5,mod=1e9+7;
int n,a[N],f[N][3][2][2],g[N][3];
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void update(int&a,const int&b){a=add(a,b);}
int main(){
	for(ri tt=read(),ans;tt;--tt){
		n=read();
		for(ri i=1;i<=n;++i){
			a[i]=read();
			for(ri j=0;j<3;++j)g[i][j]=0;
			for(ri j=0;j<3;++j)for(ri k=0;k<2;++k)for(ri l=0;l<2;++l)f[i][j][k][l]=0;
		}
		for(ri i=0;i<=a[1];++i)f[1][i][1][1]=1;
		ans=0;
		for(ri i=1;i<=n;++i){
			for(ri j=0;j<2;++j)for(ri k=0;k<2;++k)update(ans,f[i][0][j][k]);
			for(ri j=0;j<3;++j)update(ans,g[i][j]);
			for(ri j=0;j<2;++j)for(ri k=0;k<2;++k)if(f[i][1][j][k])for(ri l=0;l<=a[i+1];++l)update(f[i+1][l][1][j],f[i][1][j][k]);
			if(a[i+1]==2)update(g[i+1][2],g[i][1]);
			if(a[i]==1)continue;
			for(ri j=0;j<2;++j)for(ri k=0;k<2;++k)if(f[i][2][j][k]){
				update(g[i+2][1],f[i][2][j][k]);
				for(ri l=0;l<=a[i+2];++l)update(f[i+2][l][0][1],f[i][2][j][k]);
				if(a[i+1]==2)for(ri l=0;l<=a[i+3];++l)update(f[i+3][l][1][1],f[i][2][j][k]);
			}
			if(a[i+1]==2)update(g[i+2][1],g[i+1][2]);
		}
		cout<<ans<<'\n';
	}
	return 0;
}

Partition into Permutations

传送门
考虑贪心,每次从后往前尽量消去连续一段是最优的,用双向链表维护一下序列即可。
代码:

#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const int N=1e6+5;
int n,a[N],cnt[N],pre[N],nxt[N],q[N],m,ans;
inline void solve(){
	int ret=q[m]-m,sum=ret;
	for(ri r=m,l;r;r=l-1){
		l=r;
		while(l!=1&&q[l-1]==q[l]-1)--l;
		sum=sum-(q[l]-q[l-1]-1)+(r-l+1);
		ret=min(ret,sum);
	}
	ans+=ret;
}
int main(){
	for(ri tt=read(),tot=0,det=0;tt;--tt,tot=0,det=0){
		n=read(),ans=0;
		for(ri i=1;i<=n;++i)a[i]=read();
		sort(a+1,a+n+1);
		for(ri i=1;i<=n;++i)if(a[i]^a[tot])a[++tot]=a[i],cnt[tot]=1;else ++cnt[tot];
		n=1;
		for(ri i=1;i<=tot;++i)pre[i]=i-1,nxt[i]=i+1;
		while(n<=tot){
			m=0;
			int p=n;
			while(p<=tot){
				q[++m]=a[p];
				--cnt[p];
				if(!cnt[p])nxt[pre[p]]=nxt[p],pre[nxt[p]]=pre[p];
				p=nxt[p];
			}
			solve();
			while(n<=tot&&!cnt[n])n=nxt[n];
		}
		cout<<ans<<'\n';
	}
	return 0;
}

Travelling Saleschef

传送门
a a a数组排序,然后做一个简单的存在性 d p dp dp即可。
代码:

#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const int N=1e5+5;
int n,d,a[N],tmp,ok[N];
inline bool check(){
	for(ri i=1;i<n;++i)if(a[i+1]-a[i]>d)return 1;
	return 0;
}
inline int checkl(int pos){
	if(pos<2||a[pos+1]-a[1]<=d)return 1;
	if(~ok[pos])return ok[pos];
	for(ri i=pos-1;i;--i){
		if(a[pos+1]-a[i]>d||a[pos]-a[i-1]>d)break;
		if(checkl(i-1))return ok[pos]=1;
	}
	return ok[pos]=0;
}
inline int checkr(int pos){
	if(pos>n-1||a[n]-a[pos-1]<=d)return 1;
	if(~ok[pos])return ok[pos];
	for(ri i=pos+1;i<=n;++i){
		if(a[i]-a[pos-1]>d||a[i+1]-a[pos]>d)break;
		if(checkr(i+1))return ok[pos]=1;
	}
	return ok[pos]=0;
}
int main(){
	for(ri tt=read();tt;--tt){
		n=read(),d=read(),tmp;
		for(ri i=1;i<=n;++i)a[i]=read();
		tmp=a[1];
		sort(a+1,a+n+1);
		if(check()){puts("NO");continue;}
		if(a[1]==tmp||a[n]==tmp){puts("YES");continue;}
		bool f=0;
		memset(ok,-1,sizeof(ok));
		for(ri i=1;i<=n;++i)if(a[i]==tmp){
			f|=checkl(i);
			break;
		}
		if(f)puts("YES");
		else{
			memset(ok,-1,sizeof(ok));
			for(ri i=n;i;--i)if(a[i]==tmp){
				f|=checkr(i);
				break;
			}
			puts(f?"YES":"NO");
		}
	}
	return 0;
}

Kira Loves Palindromes

传送门
枚举中心位置分奇偶讨论一下即可。
代码:

#include
#define ri register int
using namespace std;
typedef long long ll;
const int N=1005;
int f[N][N],sl[N][N],sr[N][N],len[N][N],n;
char s[N];
inline void init(){
	for(ri i=1;i<=n;++i)f[i][i]=f[i][i-1]=1;
	for(ri len=2;len<=n;++len)for(ri l=1,r=len;r<=n;++l,++r)f[l][r]=f[l+1][r-1]&(s[l]==s[r]);
	for(ri l=1;l<=n;++l){
		sl[l][l-1]=1;
		for(ri r=l;r<=n;++r)sl[l][r]=sl[l][r-1]+f[l][r];
	}
	for(ri r=n;r;--r){
		sr[r][r+1]=1;
		for(ri l=r;l;--l)sr[r][l]=sr[r][l+1]+f[l][r];
	}
}
int main(){
	scanf("%s",s+1),n=strlen(s+1);
	init();
	ll ans=0;
	for(ri i=1;i<=n;++i){
		len[i][i]=0;
		for(ri l=i-1,r=i+1;l>=1&&r<=n;--l,++r){
			if(len[l+1][r-1])len[l][r]=len[l+1][r-1]-1;
			else{
				int pl=l,pr=r;
				while(pl>=1&&pr<=n&&s[pl]==s[pr])++len[l][r],--pl,++pr;
			}
			ans+=len[l][r];
			ans+=len[l][r]*(sl[l+1][r-2]-sl[l+1][l]+sr[r-1][l+2]-sr[r-1][r]);
			if(l+2==r)ans+=len[l][r]*(len[l][r]+1);
		}
	}
	for(ri i=1;i<n;++i){
		len[i+1][i]=0;
		for(ri l=i,r=i+1;l>=1&&r<=n;--l,++r){
			if(len[l+1][r-1])len[l][r]=len[l+1][r-1]-1;
			else{
				int pl=l,pr=r;
				while(pl>=1&&pr<=n&&s[pl]==s[pr])++len[l][r],--pl,++pr;
			}
			if(l+1==r)ans+=len[l][r]*len[l][r];
			else{
				ans+=len[l][r];
				if(r-l!=1)ans+=len[l][r]*(sl[l+1][r-2]-sl[l+1][l]+sr[r-1][l+2]-sr[r-1][r]);
			}
		}
	}
	cout<<ans;
	return 0;
}

Playing with Numbers

传送门
大力猜结论:到一个点 p p p的答案的最大值等于 m o d p mod_p modp减去从根到叶子路径上所有点权的 g c d gcd gcd m o d p mod_p modp g c d gcd gcd
证明不会然而貌似水过了这道题。

#include
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
typedef long long ll;
typedef pair<int,ll> pli;
const int N=1e5+5;
vector<int>e[N];
vector<pli>ans;
int n;
ll a[N],mod[N];
inline ll readl(){
	ll ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
inline void dfs(int p,int fa,ll pre){
	int siz=0;
	pre=__gcd(pre,a[p]);
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa)continue;
		dfs(v,p,pre),++siz;
	}
	if(!siz){
		ll tmp=__gcd(mod[p],pre);
		ans.push_back(pli(p,(mod[p]/tmp-1)*tmp));
	}
}
int main(){
	for(ri tt=read();tt;--tt){
		n=read();
		for(ri i=1;i<=n;++i)e[i].clear();
		for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
		for(ri i=1;i<=n;++i)a[i]=readl();
		for(ri i=1;i<=n;++i)mod[i]=readl();
		ans.clear();
		dfs(1,0,0);
		sort(ans.begin(),ans.end());
		for(ri i=0;i<ans.size();++i)cout<<ans[i].se<<' ';
		puts("");
	}
	return 0;
}

Intersecting Paths

传送门
考虑答案如何凑出。
假设给出的链是 ( u , v ) (u,v) (u,v),那么如果另外一条链跟它只有一个交点 p p p说明那条链剩余部分在 p p p的子树中(如果交点是链顶则还要考虑 p p p子树外连通块的贡献),然后就可以预处理出 f p f_p fp表示以 p p p l c a lca lca的路径有几条,然后统计链上面的 f f f值和,再扣掉不合法部分。(详见代码)
代码:

#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const int N=250005;
typedef long long ll;
ll f[N],g[N],h[N];
int n,m,siz[N],hson[N],dep[N],top[N],fa[N];
vector<int>e[N];
void dfs1(int p){
	f[p]=siz[p]=1,hson[p]=0;
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa[p])continue;
		fa[v]=p,dep[v]=dep[p]+1,dfs1(v),f[p]+=(ll)siz[p]*siz[v],siz[p]+=siz[v];
		if(siz[v]>siz[hson[p]])hson[p]=v;
	}
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa[p])continue;
		g[v]=(ll)(siz[p]-siz[v])*siz[v];
	}
}
void dfs2(int p,int tp){
	top[p]=tp,h[p]+=f[p]-g[p];
	if(!hson[p])return;
	h[hson[p]]=h[p],dfs2(hson[p],tp);
	for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])!=hson[p]&&v!=fa[p])h[v]=h[p],dfs2(v,v);
}
inline int lca(int x,int y){
	while(top[x]^top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}
inline int lcca(int x,int t){
	while(top[x]^top[t])if(fa[top[x]]==t)return top[x];else x=fa[top[x]];
	return hson[t];
}
int main(){
	for(ri u,v,tt=read();tt;--tt){
		n=read(),m=read();
		for(ri i=1;i<=n;++i)e[i].clear();
		for(ri i=1;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
		h[1]=0,dfs1(1),dfs2(1,1);
		while(m--){
			u=read(),v=read();
			if(u==v){
				ll ans=f[u]+(ll)siz[u]*(n-siz[u]);
				cout<<ans<<'\n';
				continue;
			}
			int t=lca(u,v),s,a,b;
			if(t==u)swap(u,v);
			if(t==v){
				ll ans=h[u]-h[t];
				s=lcca(u,t);
				a=siz[t]-siz[s];
				ans+=f[t]+(ll)a*(n-siz[t]);
				cout<<ans<<'\n';
				continue;
			}
			ll ans=h[u]+h[v]-2*h[t];
			s=lcca(u,t),a=lcca(v,t);
			ans+=(ll)siz[s]*siz[a];
			b=siz[t]-siz[s]-siz[a];
			ans+=f[t]+(ll)b*(n-siz[t]);
			cout<<ans<<'\n';
		}
	}
	return 0;
}

Sum and GCD

传送门
这题被我暴力 C a O CaO CaO过去了。
我是枚举了所有 a 1 a_1 a1的因数作为其中一个数列的 g c d gcd gcd
然而有一个结论:假设除了最大值和次大值的其它数 g c d = x gcd=x gcd=x,那么 a n s = max ⁡ { g c d ( x , a m a x ) + a s e c o n d _ m a x , g c d ( x , a s e c o n d _ m a x + a m a x ) } ans=\max\{gcd(x,a_{max})+a_{second\_max},gcd(x,a_{second\_max}+a_{max})\} ans=max{gcd(x,amax)+asecond_max,gcd(x,asecond_max+amax)}
于是可以 O ( n l o g n ) O(nlogn) O(nlogn)搞完(我tcl只会暴力而且这题数据贼水)
暴力代码:

#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++; 
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
} 
typedef long long ll;
const int N=10005;
int n,k;
ll s[N],x,a[N];
int main(){
	for(ri tt=read();tt;--tt){
		n=read();
		ll ans=0,det=0;
		for(ri i=1;i<=n;++i)a[i]=read(),ans+=a[i];
		k=read(),x=read();
		if(!x){
			cout<<ans<<'\n';
			continue;
		}
		for(ri i=1;i<=n;++i)a[i]=(a[i]^x)-a[i];
		sort(a+1,a+n+1),reverse(a+1,a+n+1);
		for(ri i=1;i<=n;++i)s[i]=s[i-1]+a[i];
		if(k==n){
			cout<<ans+max(0ll,s[n])<<'\n';
			continue;
		}
		for(ri i=2;i<=n;i+=2)det=max(det,s[i]);
		if(k&1)for(ri i=1;i<=n;++i)det=max(det,s[i]);
		cout<<det+ans<<'\n';
	}
	return 0;
}

Chef and Ingredients

传送门
简单推一推发现空段满足等差数列求和公式。
然后就完了 Q w Q QwQ QwQ
代码:

#include
#define ri register int
using namespace std;
const int rlen=1<<18|1;
typedef long long ll;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline ll read(){
	ll ans=0;
	char ch=gc();
	bool f=1;
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return f?ans:-ans;
}
const int N=1e5+5;
const ll mod=1e9+7;
ll k,n;
int main(){
	ll inv=(mod+1)/2;
	for(ri tt=read();tt;--tt){
		n=read(),k=read();
		if(n>=k){
			cout<<(int)((k-1)%mod)<<'\n';
			continue;
		}
		ll t=k;
		t-=n;
		t=t%(n-1)?t/(n-1)+1:t/(n-1);
		++t;
		ll a=k-1,b=a-(t-1)%mod*(n-1)%mod;
		a%=mod,b=(b%mod+mod)%mod;
		cout<<(int)((a+b)%mod*inv%mod*(t%mod)%mod)<<'\n';
	}
	return 0;
}

你可能感兴趣的:(#,题解)