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

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");
    }
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值