2019.08.01比赛总结

本文放代码仅供已做出本题的人交流参考使用,禁止抄袭!!!

今天比赛凉了~~~

Total Mark:18.2+0+100=118.2( \text{Total Mark:18.2+0+100=118.2(} Total Mark:18.2+0+100=118.2(最后一题把 Online Judge \text{Online Judge} Online Judge给卡爆了,到现在还在 Pending ) \text{Pending}) Pending)

【题解】

T1: \text{T1:} T1:比赛时觉得是一道博弈论的题目,然而却给出了整个矩阵,所以和其它的博弈论题目不一样,赛时没有思路,于是判断如果整个矩阵的数全是偶数且 n \text{n} n也为偶数,则输出 L \text{L} L,否则输出 W( \text{W(} W(竟然还有 18.2pts!!!) \text{18.2pts!!!)} 18.2pts!!!)。正解是 DP( \text{DP(} DP(我感觉是递推 ) \text{)} ),设 f i , j f_{i,j} fi,j表示前 i i i j j j列的矩阵是否存在必胜策略,那么易得如果 f i − 1 , j f_{i-1,j} fi1,j必败,且第 i i i行前 j j j个数的和是偶数,则 f i , j f_{i,j} fi,j必胜。对于 f i , j − 1 f_{i,j-1} fi,j1同理。
:对于一段数的和需使用二维前缀和 s u m i , j = s u m i − 1 , j + s u m i , j − 1 − s u m i − 1 , j − 1 + a i , j sum_{i,j}=sum_{i-1,j}+sum_{i,j-1}-sum_{i-1,j-1}+a_{i,j} sumi,j=sumi1,j+sumi,j1sumi1,j1+ai,j,一开始判断如果 a 1 , 1 a_{1,1} a1,1是偶数,则 f 1 , 1 f_{1,1} f1,1必胜,否则必败。

代码:

#include<cstdio>
#include<cstring>
#define N 1010
using namespace std;
int t,n;
int f[N][N],sum[N][N],a[N][N];
int main()
{
	scanf("%d",&t);
	++t;
	while (--t)
	{
		memset(sum,0,sizeof sum);
		memset(f,0,sizeof f);
		scanf("%d",&n);
		for (int i=1;i<=n;++i)
			for (int j=1;j<=n;++j) 
				scanf("%d",&a[i][j]),sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+a[i][j];
		if (a[1][1]%2==0) f[1][1]=1;
		for (int i=1;i<=n;++i)
			for (int j=1;j<=n;++j)
			{
				if (!f[i-1][j] && (sum[i][j]-sum[i-1][j])%2==0) f[i][j]=1;
				if (!f[i][j-1] && (sum[i][j]-sum[i][j-1])%2==0) f[i][j]=1;
			}
		if (f[n][n]) printf("W\n");
		else printf("L\n");
	}
	return 0;
}

T2: \text{T2:} T2:比赛时没有思路,对于如何判断相邻的数不知所措,所以最后提交了输出 rand()%5+1 \text{rand()\%5+1} rand()%5+1的程序,果不其然拿了 0 \text{0} 0分。正解是暴力模拟,用一个 c \text{c} c数组, c i , j c_{i,j} ci,j表示与编号为 i i i的数相邻的第 j j j个数是什么,首先要预处理出 a n s ans ans数组,其中有几个点要注意,要前后连相邻边,每一层的环要首尾连相邻边,如果你在判外层相邻的时候先自增了 n o w now now,那么最后记得 − 1 -1 1

代码:

#include<cstdio>
#include<cstring>
#define N 10010
using namespace std;
int n,a,begin,end,t,now;
int c[N][10],cnt[N],tot[N],bz[10],cc[N];
inline int getnum()
{
	int o=0x3f3f3f3f,w;
	for (int I=1;I<=5;++I) if (o>tot[I] && !bz[I]) o=tot[I],w=I;
	return w;
}
void getans()
{
	begin=2,end=7,t=2;
	tot[1]=cc[1]=1;
	for (int i=2;i<=7;++i) c[1][++cnt[1]]=i,c[i][++cnt[i]]=1;
	for (int i=2;i<=10005;++i)
	{
		c[i][++cnt[i]]=i+1;
		c[i+1][++cnt[i+1]]=i;
		memset(bz,0,sizeof bz);
		for (int ii=1;ii<=cnt[i];++ii) bz[cc[c[i][ii]]]=1;
		cc[i]=getnum();
		++tot[cc[i]];
		if (i==begin) c[i][++cnt[i]]=end,c[end][++cnt[end]]=i,begin=end+1,end+=t*6,++t,now=begin;
		while (cnt[i]<6) c[i][++cnt[i]]=now,c[now][++cnt[now]]=i,++now;
		--now;
	}
}
int main()
{
	memset(c,0,sizeof c);
	memset(cnt,0,sizeof cnt);
	memset(tot,0,sizeof tot);
	memset(cc,0,sizeof cc);
	scanf("%d",&n);
	getans();
	while (n--)
	{
		scanf("%d",&a);
		printf("%d\n",cc[a]);
	}
	return 0;
}

T3: \text{T3:} T3:比赛时一开始自然地想先拿暴力分,打了个 30pts \text{30pts} 30pts的暴力。后来被“余数”二字开导,想到用一个桶存该序列的前缀和 % k \%k %k的余数,因为在同一个桶中的数两两为左右端点都可以组成一个合法的子序列,即第 i i i个桶的贡献是 C c n t i 2 C_{cnt_i}^2 Ccnti2 c n t i cnt_i cnti表示第 i i i个桶中数的个数,证明如下:

1 1 1 i i i的前缀和 % k \%k %k的值为 p \text{p} p
1 1 1 j ( j &gt; i ) j(j&gt;i) j(j>i)的前缀和 % k \%k %k的值也为 p \text{p} p
则原序列中 s u m ( i , j ) % k sum(i,j)\%k sum(i,j)%k的值为 0 0 0
因为 n n n个数中取出 2 2 2个数的组合种数是 C n 2 C_n^2 Cn2,所以第 i i i个桶的贡献是 C c n t i 2 C_{cnt_i}^2 Ccnti2

:最后的答案要加上 c n t 0 cnt_0 cnt0,因为对于前缀和能整除 k k k的,自己也能为一个子序列。

十年 OI \text{OI} OI一场空,不开 long long \text{long long} long long见祖宗

代码:

#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
ll t,k,n,a,now,ans;
ll cnt[1000010];
int main()
{
	scanf("%lld",&t);
	while (t--)
	{
		memset(cnt,0,sizeof cnt);
		now=ans=0;
		scanf("%lld %lld",&k,&n);
		for (int i=1;i<=n;++i) scanf("%lld",&a),now=(now+a)%k,cnt[now]++;
		for (int i=0;i<k;++i) ans+=cnt[i]*(cnt[i]-1)/2;
		printf("%lld\n",ans+cnt[0]);
	}
	return 0;
}

明天继续加油!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值