CF 8VC Venture Cup 2017 D题

D题PolandBall and Polygon

题意就是给一个n个顶点的简单凸多边形,从第一个顶点出发向第k+1个顶点连一条线段,同时输出此时多边形被分割为几部分,接下来再从第k+1个点出发向k+1 + k 点 连 一条线段,再求此时被分割为几部分。(如果超过第n点,就再从第一个点开始)如此循环操作n次。输入中的 n k互质,从而保证该多边形每个顶点都会到过一次而且前n次不会重复到同一个点。

画一个图分析一下可以看出来,从一个点到另一个点连线产生的贡献,只和中间经过的点是否已经有了连线有关系,如图

(不知道为什么把图片转正了之后上传上来还是倒的,治好多年的颈椎病) 其实是和中间哪些点有连线和连线的数目有关系,由于每个点会有入度出度两条线(暂且这么叫吧),要注意不能经过这个点后马上给该点加上值。又是如图:

由于从某一个点出发往下一个点连线,不可能与上个点与该点连线相交叉。因此我们在到达下一个点时,再把值加给上一个点(如到达第三个点时,再给第一个点加上1, 同时给第6个点加1,到达下一个点时再给第6个点加上1,同时给第3个点加上1)

每一条连线产生的分隔贡献=上一条线的分隔数+【中间经过的所有点(不包括出发点和到达点)的值的和】+1

由于数字较大,我们用一个树状数组或者线段树来维护即可

#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<iostream>
using namespace std;
#define  LONG long long
const LONG   INF=0x3f3f3f3f;
const LONG MOD=1e9+7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
LONG ans[1000100];
LONG tree[1000100];
LONG n;
LONG lowbit(LONG x)
{
    return x & ( -x);
}
void add(LONG x,LONG val)
{
    while(x<=n)
    {
        tree[x] +=val;
        x +=lowbit(x);
    }
}
LONG sum(LONG x)
{
    LONG  tot = 0;
    while(x>0)
    {
        tot += tree[x];
        x -= lowbit(x);
    }
    return tot ;
}
int  main()
{
    LONG k;
    cin>>n>>k;
    clr0(num);
    clr0(tree);
    num[1] = 1;
    ans[1] = 2;
    LONG pre = 1 + k;
    LONG pree = 1;
    for(LONG i = 2; i<=n ;++ i)
    {
        ans[i] = ans[i-1] + 1;
        LONG now = pre + k;
//        cout<<now<<endl;
        if(now > n)
        {
            LONG res1= 0 , res2 = 0;
            LONG tmp1 = sum( now - n -1 ) ;
            LONG tmp2 = sum( n) - sum(pre );
            res1 += tmp1;
            res1 += tmp2 ;
            res2 = sum( pre - 1) - sum( now - n );
            ans[i] += min(res1 , res2);
            now -= n;
            add(pree , 1);
            add(pre , 1);
            pree = pre ;
            pre = now ;
        }
        else
        {
            LONG res1= 0 , res2 = 0;
            LONG tmp1 = sum( pre  -1 ) ;
            LONG tmp2 = sum( n) - sum( now );
            res1 += tmp1;
            res1 += tmp2 ;
            res2 = sum( now -1 ) - sum( pre  );
            ans[i] +=min(res1 , res2);
            add(pre, 1);
            add(pree, 1);
            pree = pre;
            pre = now ;
        }

    }
    for(LONG i = 1; i< n ;++ i)printf("%lld ",ans[i]);
    cout<<ans[n]<<endl;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值