zoukankan      html  css  js  c++  java
  • 某二分图匹配题的题解

    P1027 叶片

    传送门

    题意简化

    给出 n 个点的转盘,有一些点缺失,你需要再去掉一些点使得转盘重心稳定

    那么其实就是要取出一些点和缺失的点构成一个稳定的转盘,这样的话所有取出的点肯定可以表示成多个正多边形

    于是这道题可以简化为给定 (n) 个点,然后你用边数为 (n) 的约数的正多边形去框住所有的标记点,每个点最多被框一次,求框住的最少未标记点,那么未标记点最少其实就是总共框住的点数最少

    (以下所说的多边形默认为正多边形,并且会出现 2 边形,默认它存在,退化为线)

    最优选择

    题目中数据范围说 (n) 最多有两个质因子

    那么我们先考虑只有一个质因子怎么办

    假设 (n=8) 那么我们可以用边数为 2 4 8 的多边形去框点,但其实只用边数为 2 的多边形去框就好了

    因为 4 8 都有因子 2 ,可以用 2 表示出来

    所以我们只要每次找一个标记点,如果未访问就以当前点为端点开始框多边形就好了

    然后我们考虑两个质因子的情况

    假设 (n = 12)

    那么 6 边形会比 2、3 边形优么?

    显然不会,证毕

    处理方法

    所以我们的问题就是怎么用这两个正多边形去框住所有的标记点

    标算是二分图求最大独立集,但其实并不用那么麻烦

    我们令 (p1 ,p2) 为 n 的两个质因子

    那么我们可以发现把 n 个点分成 (n/(p1·p2)) 份后(也就是隔 (p1·p2) 个点选一个点,总共选 (n/(p1·p2)) 次),这几份点无论如何也不会互相影响,然后我们对于这几份点分别处理就好了

    然后我们考虑处理的方式就是对于一份点算出用 (p1) 边形 框的和用 (p2) 边形 框所会框住的点数,然后贪心取小的就行了

    正确性

    我们发现在长度为 (p1· p2) 的一份点以内一旦选择了一种正多边形去框点,另一种正多边形就会受到影响(即无法放置任何一个另一种正多边形)

    那样我们就可以分别计算两个正多边形需要框的最少点数了

    处理一种正多边形框点的答案

    考虑从未访问的标记点暴力去跑,每次跳正多边形边数个点,然后沿路标记已访问,并累加答案

    这样做的复杂度是 (n/p * p =n) 的,所以复杂度就有了保证

    代码实现

    //by Judge
    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define fp(i,a,b) for(int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(int i=(a),I=(b)-1;i>I;--i)
    using namespace std;
    const int M=2e4+3;
    #ifndef Judge
    #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    #endif
    char buf[1<<21],*p1=buf,*p2=buf;
    inline int Min(int a,int b){return a<b?a:b;}
    inline int read(){ int x=0,f=1; char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    	for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
    } int n,m,tmp,ans,num,p[5],vis[M],a[M]; vector<int> vec[M];
    inline void solv1(){ int now;  //可以判一个质因子的情况
    	fp(i,1,m) if(!vis[a[i]])
    		fp(j,1,p[1])
    			now=a[i]+(n/p[1])*j,
    			now=now%n?now%n:n,
    			vis[now]|=1,++ans;
    	if(ans==n) puts("-1");
    	else printf("%d
    ",ans-m);
    }
    inline void solv2(){  //两个质因子的情况
    	int p1=p[1],p2=p[2],now=n/p1/p2;
    	fp(i,1,m) vec[a[i]%now].push_back(a[i]);
    	fp(j,0,now-1){ int res1=0,res2=0;
    		fp(i,0,vec[j].size()-1)
    			if(!vis[vec[j][i]]) fp(k,1,p1){
    				now=vec[j][i]+(n/p1)*k,
    				now=now%n?now%n:n,
    				vis[now]|=1,++res1;
    			}
    		fp(i,0,vec[j].size()-1) vis[vec[j][i]]=0;
    		
    		fp(i,0,vec[j].size()-1)
    			if(!vis[vec[j][i]]) fp(k,1,p2){
    				now=vec[j][i]+(n/p2)*k,
    				now=now%n?now%n:n,
    				vis[now]|=1,++res2;
    			}
    		ans+=Min(res1,res2);  //贪心取最小
    	}
    	if(ans==n) puts("-1");
    	else printf("%d
    ",ans-m);
    }
    int main(){ tmp=n=read(),m=read();
    	fp(i,1,m) a[i]=read();
    	for(int i=2;i*i<=tmp;++i)if(!(tmp%i))
    		for(p[++num]=i;!(tmp%i);tmp/=i);
    	if(tmp>1) p[++num]=tmp;
    	if(!p[2]) return solv1(),0;
    	else return solv2(),0;
    }
    
  • 相关阅读:
    python之 网络编程、异常处理、面向对象
    python sys模块、mongodb操作、接口开发、程序分目录、压缩解压文件
    linux安装redis完整步骤
    python之:map、filter、random、datatime,写日志,发邮件,操作mysql、redis,MD5加密,写excel
    函数、模块
    python json处理、集合操作、函数定义、文件读写
    python字典、list、字符串常见处理方法、元组、文件读写
    学习笔记
    接口测试笔记
    【04】Cent OS 7 中部署JDK + Tomcat 环境
  • 原文地址:https://www.cnblogs.com/Judge/p/10578631.html
Copyright © 2011-2022 走看看