[codeforces 1337F] Yui and Mahjong Set 公式推导+交互式程序的测试

Codeforces Round #635 (Div. 2)   比赛人数14830

[codeforces 1337F]   Yui and Mahjong Set   公式推导+交互式程序的测试

总目录详见https://blog.csdn.net/mrcrack/article/details/103564004

在线测评地址https://codeforces.com/contest/1337/problem/F

ProblemLangVerdictTimeMemory
D - Yui and Mahjong Set GNU C++17Accepted31 ms0 KB

说明:下面呈现的内容,比较繁重,若要看懂,希望读者能拿出纸笔,进行跟随。

做到这个份上的题目,不是轻易就能弄明白的

样例理解如下

Input:
5
1 6
2 9
5 12
5 24
6 24
Output:
+ 1
+ 1
+ 2
+ 5
! 2 1 3 0 2

样例模拟如下
1 6
可以猜测1*2*3=6

+ 1 
2 9
可以猜测1*(2+1)*3=9 现可确定初始数组1的个数是2,
可以推测straight是(1,2,3),定初始数组4的个数是0

+ 1
5 12
1*(2+1+1)*3=12  C(4,3)+C(3,3)=5,此次交互对猜测结果无任何贡献,属无效交互

+ 2
5 24
(1+1)*(2+1+1)*3=24,现可确定初始数组2的个数是1,可确定初始数组3的个数是3

+ 5
6 24
C(4,3)+C(3,3)+C(3,3)=6,现可确定初始数组5的个数是2

样例是弄明白了,但感觉上述过程,毫无章法可言。

突然想到,既然投机取巧不成,还不如死板些,按1,2,3,......,n的顺序方式插入数值,看看对triplet数量的影响,对straight数量的影响

ans[i]代表值为i的数量

可先精确算出ans[1],ans[2],ans[3]

之后利用递推关系,算出ans[4],ans[5],......,ans[n-1]

最后再利用公式算出ans[n];

该题关键技巧是。两次插入1,精确算出ans[1]

1.1若加入数值i,triplet数量有变动,那就太好了,初始数组中i的数量n就确定了,计算如下

C(n+1,3)-C(n,3)=triplet数量的变动

(n+1)*n*(n-1)/(3*2)-n*(n-1)*(n-2)/(3*2)=n(n-1)/2

即n(n-1)/2=triplet数量的变动,这个时候,就没有straight数量变动的事了。

谈一下ans[1]=m,m的求法

以下针对triplet数量

0什么都不加
C(m,3)+其他

1第一次加入1
C(m+1,3)+其他
0->1对应的变化量
C(m+1,3)-C(m,3)

(n-1)第一次加入(n-1)
C(m+1,3)+其他量

n第二次加入1
C(m+2,3)+其他量
n-1->n对应的变化量
C(m+2,3)-C(m+1,3)

(n-1)->n对应的变化量 与 0->1对应的变化量 的差值
(C(m+2,3)-C(m+1,3))-(C(m+1,3)-C(m,3))
=C(m+2,3)+C(m,3)-2*C(m+1,3)
=(m+2)*(m+1)*m/6+m*(m-1)*(m-2)/6-2(m+1)*m*(m-1)/6
=m

可见ans[1]精确可求。

1.1若加入数值i,triplet数量无变动,那么可以推断,初始数组中i的数量是0或1

2.此时,就需要查看straight数量的变动了,

因straight数量变动,研究起来,比较复杂,故考虑,记录交互过程,之后离线处理

两遍处理,第一遍,处理triplet数量变动,精确算出,引起triplet数量变动的i的初始数量

若加入的i值引起triplet数量变动,那么i的初始数量,精确可求。

注意,若加入的i值未引起triplet数量变动,那么i的数量只有两种情况0或者1

第二遍,处理straight数量的变动,算出剩下的i值的数量

以7个数为例,讨论straight数量

0什么都不加
ans[1]*ans[2]*ans[3]+ans[2]*ans[3]*ans[4]+ans[3]*ans[4]*ans[5]+
ans[4]*ans[5]*ans[6]+ans[5]*ans[6]*ans[7]
1加入数值1
(ans[1]+1)*ans[2]*ans[3]+ans[2]*ans[3]*ans[4]+ans[3]*ans[4]*ans[5]+
ans[4]*ans[5]*ans[6]+ans[5]*ans[6]*ans[7]
0->1对应的变化量
ans[2]*ans[3]

2加入数值2
(ans[1]+1)*(ans[2]+1)*ans[3]+(ans[2]+1)*ans[3]*ans[4]+ans[3]*ans[4]*ans[5]+
ans[4]*ans[5]*ans[6]+ans[5]*ans[6]*ans[7]
1->2对应的变化量
(ans[1]+1)*ans[3]+ans[3]*ans[4]   可求a[3]

3加入数值3
(ans[1]+1)*(ans[2]+1)*(ans[3]+1)+(ans[2]+1)*(ans[3]+1)*ans[4]+
(ans[3]+1)*ans[4]*ans[5]+ans[4]*ans[5]*ans[6]+ans[5]*ans[6]*ans[7]
2->3对应的变化量
(ans[1]+1)*(ans[2]+1)+(ans[2]+1)*ans[4]+ans[4]*ans[5]

4加入数值4
(ans[1]+1)*(ans[2]+1)*(ans[3]+1)+(ans[2]+1)*(ans[3]+1)*(ans[4]+1)+
(ans[3]+1)*(ans[4]+1)*ans[5]+(ans[4]+1)*ans[5]*ans[6]+ans[5]*ans[6]*ans[7]
3->4对应的变化量
(ans[2]+1)*(ans[3]+1)+(ans[3]+1)*ans[5]+ans[5]*ans[6]

5加入数值5
(ans[1]+1)*(ans[2]+1)*(ans[3]+1)+(ans[2]+1)*(ans[3]+1)*(ans[4]+1)+
(ans[3]+1)*(ans[4]+1)*(ans[5]+1)+(ans[4]+1)*(ans[5]+1)*ans[6]+
(ans[5]+1)*ans[6]*ans[7]
4->5对应的变化量
(ans[3]+1)*(ans[4]+1)+(ans[4]+1)*ans[6]+ans[6]*ans[7]

6加入数值6
(ans[1]+1)*(ans[2]+1)*(ans[3]+1)+(ans[2]+1)*(ans[3]+1)*(ans[4]+1)+
(ans[3]+1)*(ans[4]+1)*(ans[5]+1)+(ans[4]+1)*(ans[5]+1)*(ans[6]+1)+
(ans[5]+1)*(ans[6]+1)*ans[7]
5->6对应的变化量
(ans[4]+1)*(ans[5]+1)+(ans[5]+1)*ans[7]   可求a[7]


7加入数值1
(ans[1]+1+1)*(ans[2]+1)*(ans[3]+1)+(ans[2]+1)*(ans[3]+1)*(ans[4]+1)+
(ans[3]+1)*(ans[4]+1)*(ans[5]+1)+(ans[4]+1)*(ans[5]+1)*(ans[6]+1)+
(ans[5]+1)*(ans[6]+1)*(ans[7]+1)
6->7对应的变化量
(ans[2]+1)*(ans[3]+1)   可求a[2]

有一个疑问,交互式程序,在本机上,如何测试,或者说调试吧。

代码调试过程,上述问题,就解决了。

需要按编程者自己的算法,构造出相应的数据,即可调试

将样例改造成,对应之后AC代码的测试数据如下

Input:
5
1 6
2 9
2 18
5 24
5 40
8 48
Output:
+ 1
+ 2
+ 3
+ 4
+ 1
! 2 1 3 0 2

上述测试数据,获得的过程如下

5
1 6    (1,1,2,3,3,3,5,5)  C(3,3)=1  2*1*3=6
2 9    (1,1,1,2,3,3,3,5,5)  C(3,3)+C(3,3)=2  3*1*3=9
2 18   (1,1,1,2,2,3,3,3,5,5)  C(3,3)+C(3,3)=2  3*2*3=18
5 24   (1,1,1,2,2,3,3,3,3,5,5)  C(3,3)+C(4,3)=5  3*2*4=24
5 40   (1,1,1,2,2,3,3,3,3,4,5,5)  C(3,3)+C(4,3)=5  3*2*4+2*4*1+4*1*2=40
8 48   (1,1,1,1,2,2,3,3,3,3,4,5,5)  C(4,3)+C(4,3)=8  4*2*4+2*4*1+4*1*2=48

AC代码如下

#include <stdio.h>
#define maxn 105
int n,ans[maxn],cnt;
struct node{
	int x,y;
}a[maxn];
void ask(int k){//交互
	printf("+ %d\n",k);
	fflush(stdout);
	cnt++;
	scanf("%d%d",&a[cnt].x,&a[cnt].y);//交互式时,要小心,不能将cnt合并进来,写成scanf("%d%d",&a[++cnt].x,&a[cnt].y);
}
int main(){
	int i;
	scanf("%d",&n);
	scanf("%d%d",&a[0].x,&a[0].y);
	for(i=1;i<=n;i++)ans[i]=-1;//初始化
	for(i=1;i<=n-1;i++)ask(i);
	ask(1);//为了精确求ans[1],而询问
	for(i=n;i>=1;i--)a[i].x-=a[i-1].x,a[i].y-=a[i-1].y;//求triplet,straight变化量
	ans[1]=a[n].x-a[1].x;
	for(i=2;i<=n-1;i++)
		if(a[i].x){//根据triplet进行精确求解
			ans[i]=0;
			while(ans[i]*(ans[i]-1)/2<a[i].x)ans[i]++;
		}
	if(ans[3]==-1)ans[3]=(a[2].y>0);//ans[3]精确可求
	if(ans[2]==-1)ans[2]=a[n].y/(ans[3]+1)-1;//ans[2]精确可求
	for(i=3;i<=n-2;i++)
		if(ans[i+1]==-1){//可求4,5,6,......,n-1
			a[i].y-=(ans[i-1]+1)*(ans[i-2]+1);
			ans[i+1]=(a[i].y>0);
		}
	ans[n]=(a[n-1].y-(ans[n-2]+1)*(ans[n-3]+1))/(ans[n-2]+1);//求ans[n];
	printf("!");
	for(i=1;i<n;i++)printf(" %d",ans[i]);
	printf(" %d\n",ans[n]);
	return 0;
}

通过该题,第一次见识了交互式程序,总体来说,造数据是比较耗时的

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值