我真的不想写背景

我真的不想写背景


题目描述

某巨魔有一风扇,长得和下图一个怂样。

这里写图片描述

这风扇原来有 N 个等分叶片,顺时针编号 1 N (谁是编号 1 这不重要)。
任意两个相邻的叶片间隔相等,大小质地相同。
这个 N 满足 N=PaQb P , Q 为质数, a , b 为自然数。
风扇一开始还不错,但是到后来风扇开始报复社会了。
它的叶片从窗户甩了出去甩到了大街上,砸肿了二胖(二胖以前身材很好,你懂得)。
风扇的叶片脱落是很危险的,因为这会使风扇失去平衡。
可能当你经过风扇下方时它就直接拿你一血。
但是左图风扇是平衡的,因为他的重心在风扇的正中心(至于为什么自己想吧)。
现在给你一烂风扇。
需要你判断它平不平衡,如果不平衡,那么判断它最少要拆掉它的多少叶片才能平衡。


输入格式

第一行两个数字 N M,表示该风扇原来有 N 个叶片现在已经脱落了 M 个叶片。
接下来 M 行,每行一个数字,表示脱落的叶片编号,按升序给出。


输出格式

一个数字,表示为使电风扇平衡所需要拆掉的叶片的最少数量。


样例输入

12 5
1
4
5
9
10


样例输出

0


样例解释

拆完后就是上图那怂电风扇。


数据范围

30% 的数据: 1N10
50% 的数据: 1N10000
100% 的数据: 1N2106 MN


Solution

首先,图中这个风扇为什么平衡成了一大问题,搞出了这个那么这个题就简单了(蒟蒻我是想了很久很久很久很久……)
因为一对对称的风扇叶肯定是平衡的,如果把它们拿掉也不影响风扇的平衡性。
于是……我就把所有对称的风扇叶拿掉了。
于是乎变成了下面这个图:

这里写图片描述

这是个三等分的风扇,肯定是对称的。
所以原风扇是平衡的………
所以,我们就要在一个风扇中,找到一个质数 k ,使得这 n 个扇叶中,存在一个 k 等分的组合。
那么,n 肯定是 k 的倍数,而 k 又是质数,所以 k=p q

列出所有等分的风扇。
p 等分的风扇:

风扇序号第一个扇叶第二个扇叶第三个扇叶第四个扇叶……最后一个扇叶
风扇1 1+np 1+2np 1+3np 1+4np 1+(p1)np
风扇2 2+np 2+2np 2+3np 2+4np 2+(p1)np
风扇3 3+np 3+2np 3+3np 3+4np 3+(p1)np
……
风扇 np np+np np+2np np+3np np+4np n

q 等分的扇叶:

风扇序号第一个扇叶第二个扇叶第三个扇叶第四个扇叶……最后一个扇叶
风扇1 1+nq 1+2nq 1+3nq 1+4nq 1+(q1)nq
风扇2 2+nq 2+2nq 2+3nq 2+4nq 2+(q1)nq
风扇3 3+nq 3+2nq 3+3nq 3+4nq 3+(q1)nq
……
风扇 nq nq+nq nq+2nq nq+3nq nq+4nq n

注意到所有 p 等分的风扇互不相交,所有 q 等分的风扇也互不相交。
新建一个源点,向每一个 p 等分的风扇连一条容量为 p 的边;
新建一个汇点,每一个 q 等分的风扇向汇点连一条容量为 q 的边;

若某个 p 等分的风扇与某个 q 等分的风扇中有一个或几个扇叶相同,则从这一个 p 等分的风扇连一条容量为 INF 的边到这一个 q <script type="math/tex" id="MathJax-Element-85">q</script> 等分的风扇。
要求最少拿去多少个扇叶能使其平衡,等价于拿去若干个等分的风扇,使得剩下来的扇叶最少。
因为一个扇叶显然只能放在一个风扇中被拿走,所以问题等价于求我们建的图的最小割的补集。
求出最大流即可。


Code

#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>

#define Min(x,y) ((x)<(y)?(x):(y))

#define s 0
#define t 2000005
#define INF 0x3f3f3f3f

using namespace std;

int n,m,p,q,tot,cnt;

bool w[2000010];
bool vis[2000010];
int in[2000010];

int head[2000010],nxt[8000010],data[8000010],flow[8000010],dis[2000010];
int cur[2000010];
queue<int>qs;

void add(int x,int y,int z){
    nxt[cnt]=head[x];data[cnt]=y;flow[cnt]=z;head[x]=cnt++;
    nxt[cnt]=head[y];data[cnt]=x;flow[cnt]=0;head[y]=cnt++;
}

bool BFS(){
    memset(dis,-1,sizeof dis);
    qs.push(s);dis[s]=0;
    while(!qs.empty()){
        int now=qs.front();
        qs.pop();
        for(int i=head[now];i!=-1;i=nxt[i])if(dis[data[i]]==-1&&flow[i]){
            dis[data[i]]=dis[now]+1;
            qs.push(data[i]);
        }
    }
    return dis[t]!=-1;
}

int dinic(int now,int Flow){
    if(now==t)return Flow;
    int Low;
    for(int &i=cur[now];i!=-1;i=nxt[i])if(dis[data[i]]==dis[now]+1&&flow[i]){
        if(Low=dinic(data[i],Min(Flow,flow[i]))){
            flow[i]-=Low;
            flow[i^1]+=Low;
            return Low;
        }
    }
    return 0;
}

int main(){

    freopen("damn.in","r",stdin);
    freopen("damn.out","w",stdout);

    int maxn=0;

    memset(head,-1,sizeof head);
    scanf("%d%d",&n,&m);int tn=n;
    for(int i=1;i<=m;i++){
        int x;
        scanf("%d",&x);
        w[x]=true;
    }
    bool first=true;
    int sqr=sqrt(n);
    for(int i=2;i<=sqr;i++)
        if(n%i==0&&first){
            first=false;
            p=i;
            while(n%i==0)n/=i;
        }
        else if(n%i==0){
            q=i;
            while(n%i==0)n/=i;
            break;
        }
    if(n!=1)q=n;n=tn;
    for(int i=1;i<=n/p;i++)if(!w[i]){
        for(int j=i;j<=n;j+=n/p)if(w[j])goto nxt;
        ++tot;
        maxn+=p;
        add(s,tot,p);
        for(int j=i;j<=n;j+=n/p){
            in[j]=tot;
        }
        nxt:;
    }
    for(int i=1;i<=n/q;i++)if(!w[i]){
        for(int j=i;j<=n;j+=n/q)if(w[j])goto nxt2;
        ++tot;
        maxn+=q;
        for(int j=i;j<=n;j+=n/q)vis[in[j]]=false;
        for(int j=i;j<=n;j+=n/q){
            if(in[j]&&!vis[in[j]]){
                vis[in[j]]=true;add(in[j],tot,INF);
            }
        }
        add(tot,t,q);
        nxt2:;
    }
    while(BFS()){
        int Low;
        memcpy(cur,head,sizeof head);
        while(Low=dinic(s,INF)){
            maxn-=Low;
        }
    }
    printf("%d\n",n-m-maxn);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值