2018-2019 Winter Petrozavodsk Camp, Oleksandr Kulkov Contest 1 解题报告

E


签到题,看起来像是博弈论,其实仔细思考后发现,一个石子堆为偶数一定给两人贡献相同,实际上对答案有贡献的是奇数堆,那么只要统计奇数堆的个数就行了。


#include <bits/stdc++.h>

using namespace std;

int input(){
	int x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

int n;
int a,Ans,cnt;

int main(){
	n=input();
	for(int i=1;i<=n;i++){
		a=input();
		if(a%2==1) cnt++;
		Ans+=a/2;
	}
	Ans+=cnt%2? cnt/2+1:cnt/2;
	printf("%d\n",Ans);
}

K


签到题,随便手玩一下不难发现计算答案时除去\(a_1\)\(a_2\)之间的符号无法改变,其他项之间都能找到相反的符号排列方式。


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const ll mod=1e9+7;
const int N=4007;

int a[N];

int main(){
	int n=input();
	for(int i=1;i<=n;i++){
		a[i]=input();
	}
	printf("%d\n",(a[1]-a[2]+mod)%mod);
}

H


首先策略肯定是:存在某个阈值k,使得小于等于k时就给对面,否则自己拿着。然后设期望差值为s。然后讨论第一轮的数i是啥,如果i小于等于k,那么最后的ans=s-i,因为第二轮先手不变,期望值不变。如果i大于k,那么最后的ans=i-s,因为先后手交换,期望值取反。所以有\(E(Ans)=\frac{1}{m}(\sum_{i=1}^{k}(s-i)+\sum_{i=k+1}^{m}(i-s))\)。这个式子肯定把\(k\)设为\(ans\)的时候取最大值所以有\(E(Ans)=\frac{1}{m}(\sum_{i=1}^{m}|Ans-i|)\)。(官方给的题解表达方式是\(dp_n=\frac{1}{m}(\sum_{i=1}^{m}max(dp_{n-1}-i,i-dp_{n-1}))=\frac{1}{m}(\sum_{i=1}^{m}|dp_{n-1}-i|)\)

要解这个东西我们可以先考虑二分答案的整数部分,然后再计算答案。

我们不妨设答案为\(x\)。那么有:

\[x=\frac{1}{m}(\sum_{i=1}^{\lfloor x \rfloor}(x-i)+\sum_{i=\lfloor x \rfloor+1}^{m}(i-x))\\ mx=x\lfloor x \rfloor-\frac{\lfloor x \rfloor(\lfloor x \rfloor+1)}{2}+\frac{(m+\lfloor x \rfloor+1)(m-\lfloor x \rfloor)}{2}+(\lfloor x \rfloor-m)x\\ mx=(2\lfloor x \rfloor-m)x-\frac{2\lfloor x \rfloor^2+2\lfloor x \rfloor-m^2-m}{2}\\ 2(m-\lfloor x \rfloor)x=\frac{m^2-\lfloor x \rfloor^2}{2}+\frac{m-\lfloor x \rfloor}{2}-\frac{\lfloor x \rfloor^2+\lfloor x \rfloor}{2}\\ x=\frac{m+\lfloor x \rfloor+1}{4}-\frac{\lfloor x \rfloor(\lfloor x \rfloor+1)}{4(m-\lfloor x \rfloor)} \]

所以我们可以通过\(\lfloor x \rfloor\)计算出答案。然后要得到答案可以通过二分用\(mx=sum_{mid}+sum_{m-mid}\)来获得\(\lfloor x \rfloor\)


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const ll mod=1e9+7;

ll powmod(ll a,ll b){
	ll res=1;
	while(b){
		if(b&1) res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}

ll sum(ll x){
	return x*(1+x)/2;
}

int main(){
	int T=input();
	while(T--){
		ll m=input();
		ll l=1,r=m;
		while(l<r){
			ll mid=(l+r+1)>>1;
			if(sum(mid-1)+sum(m-mid)>=mid*m) l=mid;
			else r=mid-1;
		}
		ll x1=(m+l+1)%mod;
		ll x2=l*(l+1)%mod*powmod((m-l)%mod,mod-2)%mod;
		ll Ans=((x1-x2+mod)%mod*((mod+1)/4))%mod;
		printf("%lld\n",Ans);
	}
}

F


把边按权值排序从大到小加入图中,如果加入树边,且边的端点已经连通,则包含这两端点连通块不合法;如果加入图边,且两点还未连通,那么包含这条边两端点的连通块也不合法。考虑到边权有相同的情况,需要把相同的边一并加入图中。


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

#define pb push_back

const int N=1e5+7;

struct bcj{
	int fa[N],rank[N],col[N];
	int find(int x){
		if(fa[x]==x) return x;
		int t=find(fa[x]);
		if(col[fa[x]]||col[t]) col[x]=1;
		fa[x]=t;
		return t;
	} 
	void merge(int x,int y){
		x=find(x);y=find(y);
		if(col[x]<col[y]) swap(x,y);
		fa[x]=y;
		if(x!=y) rank[y]+=rank[x];
	}
	
}t,g;

struct edge{
	int t,u,v,w;
}e[N*2];
int n,m;

bool cmp(edge a,edge b){
	if(a.w!=b.w) return a.w>b.w;
	if(a.t!=b.t) return a.t>b.t;
	return 0;
}

int main(){
	n=input(),m=input();
	for(int i=1;i<=m;i++){
		e[i].t=input(),e[i].u=input(),e[i].v=input(),e[i].w=input();
	}

	for(int i=1;i<=n;i++){
		g.fa[i]=t.fa[i]=i;
		g.rank[i]=t.rank[i]=1;
	}

	sort(e+1,e+1+m,cmp);

	for(int i=1;i<=m;){
		int j=i;
		while(j<=m&&e[i].w==e[j].w){
			if(e[j].t==1){
				g.merge(e[j].u,e[j].v);
				t.merge(e[j].u,e[j].v);
			}else g.merge(e[j].u,e[j].v);
			j++;
		}

		for(int k=i;k<j;k++){
			int t1=g.find(e[k].u),t2=t.rank[t.find(t1)];
			if(t2!=g.rank[t1]) g.col[t1]=1;
		}
		i=j;
	}

	vector <int> ans;
	for(int i=1;i<=n;i++){
		g.find(i);
		if(g.col[i]==0&&g.col[g.fa[i]]==0) ans.pb(i);
	}
	printf("%d\n",ans.size());
	for(int i=0;i<ans.size();i++){
		printf("%d%c",ans[i],i==ans.size()-1? '\n':' ');
	}
}

posted @ 2020-03-19 16:34  _aether  阅读(492)  评论(0编辑  收藏  举报