COCI 2016/2017 Round #2 题解

COCI 2016/2017 Round #2

Go

题目翻译

COCI 2016/2017 Round #2 题解_第1张图片

分析

注意题目是先提供糖果再返还,所以我们不能直接硬除。

按题意暴力模拟即可,复杂度最高只有约 4 × 1 0 5 4\times 10^5 4×105,可以过。

参考代码

#include
#include
using namespace std;

int N;

struct Node {
	char nam[25];
	int cst,k;
	int cnt;
}A[70+5];

int main() {
	freopen("go.in","r",stdin);
	freopen("go.out","w",stdout);
	scanf("%d\n",&N);
	for(int i=1;i<=N;i++)
		scanf("%s %d %d\n",A[i].nam,&A[i].cst,&A[i].k);
	int tot=0,maxx=-1;
	for(int i=1;i<=N;i++) {
//		A[i].cnt=A[i].k/(A[i].cst-2);
//		tot+=A[i].cnt;
//		maxx=max(maxx,A[i].cnt);
		while(A[i].k>=A[i].cst) {
			A[i].k-=A[i].cst;
			A[i].cnt++;
			A[i].k+=2;
		}
		tot+=A[i].cnt;
		maxx=max(maxx,A[i].cnt);
	}
	printf("%d\n",tot);
	for(int i=1;i<=N;i++)
		if(maxx==A[i].cnt) {
			puts(A[i].nam);
			break;
		}
	return 0;
}

Tavan

题目翻译

COCI 2016/2017 Round #2 题解_第2张图片

分析

首先想到的应该是暴力回溯法。(做法不解释,自己看注释)

我们画一下解答树可以发现一些规律(以样例一为例,如下图所示):
COCI 2016/2017 Round #2 题解_第3张图片
我们在每层节点放一个编号(红字),答案路径为橙色。

不难发现最下一层编号为 X X X的节点就是答案,只要沿着路径往回就可以了。

这个操作可以简单利用除法和取余来完成,具体详见代码。

参考代码

#include
#include
#include
#include
using namespace std;

const int Maxn=500;

int N,M,K,X;
char S[Maxn+5];
char s[Maxn+5][30];
/*
vector ans;
int cnt;
void DFS(int num) {
	if(num>M) {
		cnt++;
		if(cnt==X) {
			for(int i=0,j=0;i

char ans[Maxn+5];

int main() {
	freopen("tavan.in","r",stdin);
	freopen("tavan.out","w",stdout);
	scanf("%d %d %d %d\n",&N,&M,&K,&X);
	scanf("%s\n",S);
	for(int i=1;i<=M;i++) {
		scanf("%s\n",s[i]);
		sort(s[i],s[i]+K);
	}
//	DFS(1);
	for(int i=M;i>=1;i--) {
		int t=(X-1)%K;
		ans[i]=s[i][t];
		if(X%K==0)X/=K;
		else X=X/K+1;
	}
	for(int i=0,j=1;i<N;i++) {
		if(S[i]=='#') {
			putchar(ans[j]);
			j++;
		} else putchar(S[i]);
	}
	return 0;
}

Nizin

题目翻译

COCI 2016/2017 Round #2 题解_第4张图片

分析

这题我们可以贪心地从两边往中间走,不符回文就直接加就行了。

参考代码

#include
#include
using namespace std;

typedef long long ll;
const int Maxn=1e6;

int N;
ll A[Maxn+5];

int main() {
	freopen("nizin.in","r",stdin);
	freopen("nizin.out","w",stdout);
	scanf("%d",&N);
	for(int i=1;i<=N;i++)
		scanf("%d",&A[i]);
	int tot=0;
	int l=1,r=N;
	while(l<=r) {
		if(A[l]==A[r]) {
			l++,r--;
		} else if(A[l]<A[r]) {
			A[l+1]+=A[l];
			l++,tot++;
		} else if(A[l]>A[r]) {
			A[r-1]+=A[r];
			r--,tot++;
		}
	}
	printf("%d\n",tot);
	return 0;
}

Prosjecni

题目翻译

COCI 2016/2017 Round #2 题解_第5张图片

分析

这题真的是脑洞大开。。。。

我们分两种情况讨论:

对于奇数的矩阵,只需将 1 , 2 , 3 , … , N 2 1,2,3,\ldots,N^2 1,2,3,,N2挨个填进去就可以了。

对于偶数的矩阵:

N = 2 N=2 N=2时,手推一下就可以发现不存在这样的矩阵。

N N N等于其他数时,我们可以采用如下步骤构造一个矩阵:

  1. 构造第 1 1 1行为 1 , 2 , 3 , … , N − 2 , N − 1 , N ( N − 1 ) 2 1,2,3,\ldots,N-2,N-1,\frac{N(N-1)}{2} 1,2,3,,N2,N1,2N(N1)
  2. 从第 2 2 2行开始,至第 N − 1 N-1 N1行结束,每行每个元素较上一行递增 N ( N − 1 ) 2 \frac{N(N-1)}{2} 2N(N1)
  3. 设第 N − 1 N-1 N1行第一个数据为 a 1 a_1 a1,则第 N N N行的数字较第 N − 1 N-1 N1行增加 1 + ( N − 2 ) ( N − 1 ) N ( N + 1 ) 2 − a 1 1+\frac{(N-2)(N-1)N(N+1)}{2}-a_1 1+2(N2)(N1)N(N+1)a1

接下来说明这个方法的合理性:

从第 1 1 1行到第 N − 1 N-1 N1行,各个数字显然是不相等的,且该行平均数都在于第 N − 1 N-1 N1列上。

根据构造方法,每一列的平均数也在第 N − 1 N-1 N1行上。

我们可以知道,第 N − 1 N-1 N1行最后一个数字是 N ( N − 1 ) 2 2 \frac{N(N-1)^2}{2} 2N(N1)2,第 N N N行第一个数字是 1 + ( N − 2 ) ( N − 1 ) N ( N + 1 ) 2 1+\frac{(N-2)(N-1)N(N+1)}{2} 1+2(N2)(N1)N(N+1),画一下它们的函数图像:(如下图)
COCI 2016/2017 Round #2 题解_第6张图片

可以看出两函数有一个交点 A ( 2 , 1 ) A(2,1) A(2,1),且 g ( x ) g(x) g(x)增长速度明显要比 f ( x ) f(x) f(x)大,所以我们不必担心有重复的数字。

接下来说明最大的数字不会超过 1 0 9 10^9 109

我们显然可以发现,第 N N N行第 N N N个数字是最大的,手推一下可以发现最后一个数的解析式为 ( N − 1 ) N ( N 2 − N − 1 ) 2 \frac{(N-1)N(N^2-N-1)}{2} 2(N1)N(N2N1),把极限数据 100 100 100带进去得到结果 49000050 49000050 49000050没有超过 1 0 9 10^9 109

参考代码

#include
#include
using namespace std;

int a[100+5];

int main() {
	freopen("prosjecni.in","r",stdin);
	freopen("prosjecni.out","w",stdout);
	int N;
	scanf("%d",&N);
	if(N%2) {
		for(int i=1;i<=N;i++) {
			for(int j=1;j<=N;j++)
				printf("%d ",(i-1)*N+j);
			puts("");
		}
	} else {
		if(N==2) {
			puts("-1");
			return 0;
		} else {
			int t=(N-1)*N/2;
			for(int i=1;i<N;i++)
				a[i]=i;
			a[N]=t;
			for(int i=1;i<N;i++) {
				for(int j=1;j<=N;j++) {
					printf("%d ",a[j]);
					a[j]+=t;
				}
				puts("");
			}
			int tmp=1+(N-2)*(N+1)/2*t-a[1];
			for(int i=1;i<=N;i++)
				printf("%d ",a[i]+tmp);
			puts("");
		}
	}
	return 0;
}

附送我考试时手写的一个简单的special judge (不能加到lemon中去)

#include
#include
#include
using namespace std;

const int Maxn=100;

int N;
int T[Maxn+5][Maxn+5];

int main() {
	scanf("%d",&N);
	for(int i=1;i<=N;i++) {
		for(int j=1;j<=N;j++)
			scanf("%d",&T[i][j]);
	}
	for(int i=1;i<=N;i++) {
		int t=0;
		for(int j=1;j<=N;j++)
			t+=T[i][j];
		t/=N;
		bool is_find=false;
		for(int j=1;j<=N;j++)
			if(t==T[i][j]) {
				is_find=true;
				break;
			}
		if(is_find==false) {
			puts("WA");
			return 0;
		}
	}
	for(int j=1;j<=N;j++) {
		int t=0;
		for(int i=1;i<=N;i++)
			t+=T[i][j];
		t/=N;
		bool is_find=false;
		for(int i=1;i<=N;i++)
			if(t==T[i][j]) {
				is_find=true;
				break;
			}
		if(is_find==false) {
			puts("WA");
			return 0;
		}
	}
	puts("AC");
	system("pause");
	return 0;
}

Zamjene

题目翻译

COCI 2016/2017 Round #2 题解_第7张图片

分析

这题真的绕。。。。

我们先不管操作1,2。

考虑操作3,这一句话相当于将一堆有关联的东西连在一起并互相替换。这有点像并查集。所以我们就试一下吧。

如何证明排序?我们可以尝试 O ( N 2 ) O(N^2) O(N2)暴力查询,但显然是要超时的。。。

其实我们只需要记录能交换的位置上的数,与排好的对应位置上的数比较,只要有相同数量的数,我们就可以认为这个“云”是好的,即这几个位置可以排好序。

所以我们可以考虑哈希。。。如此对于两个云我们就可以 O ( 1 ) O(1) O(1)比较了。

所以,并查集只是一个载体。。。

对于操作1,我们应先将对应位置上的数从哈希表中删掉,并交换,最后不要忘掉再加回去。

对于操作2,我们在合并时把对应的哈希值加上即可。

对于操作3,我们只需利用查找两组对应哈希值相减是否都为0即可。

对应操作4,我们需要找到一个如下所示的云:COCI 2016/2017 Round #2 题解_第8张图片
这四个云必须满足 h 1 + h 3 = h 2 + h 4 h_1+h_3=h_2+h_4 h1+h3=h2+h4,直接枚举不太好,我们移项一下:变为 h 1 − h 2 = − ( h 3 − h 4 ) h_1-h_2=-(h_3-h_4) h1h2=(h3h4),这样我们就可以将对应的云的哈希值相减存在map里,最后数一遍就行了。

具体实现还是看代码吧。。。

参考代码

#include
#include
#include
using namespace std;

typedef long long ll;
const int Maxn=1e6;
const int Hash=1e7+7;
const int Mod=1e9+7;

int N,P;
int A[Maxn+5];
int Q[Maxn+5];

ll powt[Maxn+5];
ll p[Maxn+5],q[Maxn+4];
map<ll,int> diff;
int num[Maxn+5];
ll totpair;

void adddiff(int dir,ll dif,int n) {
	if(dif!=0) {
		totpair+=dir*n*diff[-dif];
	}
	diff[dif]+=dir*n;
}
void add(int dir,int u,int pos,int val) {
	p[u]+=dir*powt[pos];
	q[u]+=dir*powt[val];
	num[u]+=dir;
}

int fa[Maxn+5];
int find(int u) {
	if(fa[u]==u)return fa[u];
	return fa[u]=find(fa[u]);
}
void merge(int a,int b) {
	int u=find(a),v=find(b);
	if(u==v)return;
	adddiff(-1,p[u]-q[u],num[u]);
	adddiff(-1,p[v]-q[v],num[v]);
	fa[v]=u;
	p[u]+=p[v];
	q[u]+=q[v];
	num[u]+=num[v];
	adddiff(1,p[u]-q[u],num[u]);
}

int main() {
	freopen("zamjene.in","r",stdin);
	freopen("zamjene.out","w",stdout);
	scanf("%d %d",&N,&P);
	for(int i=1;i<=N;i++) {
		scanf("%d",&A[i]);
		fa[i]=i;
		Q[i]=A[i];
		num[i]=1;
	}
	sort(Q+1,Q+N+1);
	powt[0]=1;
	for(int i=1;i<=Maxn;i++)
		powt[i]=powt[i-1]*Hash;
	for(int i=1;i<=N;i++) {
		q[i]=powt[A[i]];
		p[i]=powt[Q[i]];
		adddiff(1,p[i]-q[i],num[i]);
	}
	while(P--) {
		int op;
		scanf("%d",&op);
		if(op==1) {
			int a,b;
			scanf("%d %d",&a,&b);
			int u=find(a),v=find(b);
			if(u==v)
				swap(A[a],A[b]);
			else {
				adddiff(-1,p[u]-q[u],num[u]);
				adddiff(-1,p[v]-q[v],num[v]);
				add(-1,find(a),a,A[a]);
				add(-1,find(b),b,A[b]);
				swap(A[a],A[b]);
				add(1,find(a),a,A[a]);
				add(1,find(b),b,A[b]);
				adddiff(1,p[u]-q[u],num[u]);
				adddiff(1,p[v]-q[v],num[v]);
			}
		} else if(op==2) {
			int a,b;
			scanf("%d %d",&a,&b);
			merge(a,b);
		} else if(op==3) {
			puts(diff[0]==N?"DA":"NE");
		} else if(op==4) {
			printf("%lld\n",totpair);
		}
	}
	return 0;
}

Burza

题目翻译

COCI 2016/2017 Round #2 题解_第9张图片

分析

对于深度大于 K K K的点,我们都可以舍掉,因为无论玩家怎么放,硬币最多向下 K K K次。

我们称深度为 K K K的结点为叶结点,并从左向右依次编号为 1 , 2 , … , N 1,2,\ldots ,N 1,2,,N。。.。。(以下省略一万字)

(题解过于玄学,为避免阅读不适就省略,文后附官方题解截图,原比赛讨论链接)

简而言之,我们通过贪心的方法可以发现,当 K 2 ≥ N K^2\ge N K2N时,无论如何都是有解的。

K 2 < N K^2<N K2<N时,我们可以利用状压DP求出来,这部分可以参考lsk大佬的后半部分题解。

注意这道题卡内存。
COCI 2016/2017 Round #2 题解_第10张图片COCI 2016/2017 Round #2 题解_第11张图片

参考代码

#include
#include
#include
#include
using namespace std;

const int Maxn=400;

vector<int> G[Maxn+5];
void addedge(int u,int v) {
	G[u].push_back(v);
	G[v].push_back(u);
}

int N,K;

int cnt;
int dep[Maxn+5];
int fa[Maxn+5],l[Maxn+5],r[Maxn+5];
void DFS(int u,int parent,int depth) {
	dep[u]=depth,fa[u]=parent;
	if(depth==K) {
		l[u]=cnt++,r[u]=cnt;
		return;
	}
	l[u]=cnt;
	for(int i=0;i<(int)G[u].size();i++) {
		int v=G[u][i];
		if(v==parent)continue;
		DFS(v,u,depth+1);
	}
	r[u]=cnt;
}

bool f[Maxn+1][(1<<20)+1];
vector<int> h[Maxn+5];
bool DP() {
	f[0][0]=true;
	for(int i=1;i<=N;i++)
		if(dep[i])
			h[r[i]].push_back(i);
	for(int i=1;i<=cnt;i++)
		for(int j=0;j<(int)h[i].size();j++)
			for(int s=0;s<(1<<K);s++)
				if(s&(1<<(dep[h[i][j]]-1)))
					f[i][s]|=f[l[h[i][j]]][s^(1<<(dep[h[i][j]]-1))];
	for(int j=0;j<(1<<K);j++)
		if(f[cnt][j])return true;
	return false;
}

int main() {
	freopen("burza.in","r",stdin);
	freopen("burza.out","w",stdout);
	scanf("%d %d",&N,&K);
	for(int i=1;i<N;i++) {
		int u,v;
		scanf("%d %d",&u,&v);
		addedge(u,v);
	}
	if(K*K>=N) {
		puts("DA");
		return 0;
	}
	DFS(1,-1,0);
	puts(DP()?"DA":"NE");
	return 0;
}

你可能感兴趣的:(#,COCI系列比赛)