您的位置:首页 > 编程语言 > Go语言

8VC Venture Cup 2017 - Elimination Round D. PolandBall and Polygon【思维+树状数组】好题

2017-01-16 18:48 411 查看
D. PolandBall and Polygon

time limit per test
4 seconds

memory limit per test
256 megabytes

input
standard input

output
standard output

PolandBall has such a convex polygon with n veritces that no three of its diagonals intersect at the same point. PolandBall decided to improve it and draw some red segments.

He chose a number k such that
gcd(n, k) = 1. Vertices of the polygon are numbered from
1 to n in a clockwise way. PolandBall repeats the following process
n times, starting from the vertex
1:

Assume you've ended last operation in vertex
x (consider x = 1 if it is the first operation). Draw a new segment from vertex
x to k-th next vertex in clockwise direction. This is a vertex
x + k or x + k - n depending on which of these is a valid index of polygon's vertex.

Your task is to calculate number of polygon's sections after each drawing. A section is a clear area inside the polygon bounded with drawn diagonals or the polygon's sides.

Input
There are only two numbers in the input: n and
k (5 ≤ n ≤ 106,
2 ≤ k ≤ n - 2,
gcd(n, k) = 1).

Output
You should print n values separated by spaces. The
i-th value should represent number of polygon's sections after drawing first
i lines.

Examples

Input
5 2


Output
2 3 5 8 11


Input
10 3


Output
2 3 4 6 9 12 16 21 26 31


Note
The greatest common divisor (gcd) of two integers a and
b is the largest positive integer that divides both
a and b without a remainder.

For the first sample testcase, you should output "2 3 5 8 11". Pictures below correspond to situations after drawing lines.













题目大意:

给你一个N多边形,从1开始进行连线,每次和+k之后的点进行相连(now+k如果大于了n,那么就是now+k-n)。一共连线N次,问每次连线结束后,会分成了多少小区域;

思路:

1、首先对于下述思路引入几个设定:我们设定d【i】表示进行到第i次连线之前第i个点所连接的边的个数。

设定劣弧表示,对于一个弦所将圆分成两部分中的较短圆弧。

2、观察样例1的图示。

假设我们现在所有点都在一个圆上的话,

很显然增加的答案就在弦的劣弧部分增加出来的。

那么对应连线的劣弧上的所有点都没有被连过的话,显然答案就是之前的答案加一(只增加了一个区域)。

对应连线的劣弧上的某个点i一共被连了一次的话,显然答案就是之前的答案加2(穿过点i连出的线一次,将增加的区域一分为二)。

对应连线的劣弧上的某个点i一共被连了两次的话,显然答案就是之前的答案加3(穿过点i连出的线两次,将增加的区域一分为三)。

那么最终其实就是:

每次连边之后的结果就是之前的结果+连线两点所组成劣弧上的所有点(抛出去连线的这两个点)d【i】的和+1。

3、那么问题就变成了单点更新,区间求和的问题上来了。

那么接下来只要维护一个树状数组(或者线段树)来O(LogN)查询即可。

4、注意两点,如果k>n/2的时候,可能因为代码实现问题,所带来不对的结果,那么其实如果k>n/2的时候,我们可以让k=k-n/2,让其反过去走(顺时针连线变成逆时针的感觉)。

另外结果比较大,需要LL的地方不要忘记。

Ac代码:

#include<stdio.h>
#include<string.h>
using namespace std;
#define ll __int64
int tree[1000400];
ll ans[1000400];
int n;
int lowbit(int x)
{
return x&(-x);
}
int sum(int x)
{
int sum=0;
while(x>0)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
void add(int x,int c)
{
while(x<=n)
{
tree[x]+=c;
x+=lowbit(x);
}
}
int main()
{
int k;
while(~scanf("%d%d",&n,&k))
{
if(k>n/2)
{
k=n-k;
}
memset(tree,0,sizeof(tree));
memset(ans,0,sizeof(ans));
int tmp=n-1;
ll output=2;
int now=1+k;
add(1,1);
add(now,1);
int contz=0;
ans[contz++]=2;
while(tmp--)
{
int pre=now;
now+=k;
if(now>n)now-=n;
if(now>pre)
{
output+=sum(now-1)-sum(pre)+1;
}
else
{
output+=sum(n)-sum(pre)+sum(now-1)+1;
}
add(pre,1);
add(now,1);
ans[contz++]=output;
}
for(int i=0;i<contz;i++)
{
printf("%I64d ",ans[i]);
}
printf("\n");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  8VC Venture Cup