• 某二分图匹配题的题解


    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;
    }
    
  • 相关阅读:
    hdu1078 记忆化dfs
    hdu1142 dij+记忆化深搜
    UVA 11374 dijkstra预处理+枚举
    poj1502 单源最短路径
    hdu1814 2-SAT 暴力搜
    macos 10.15.1 pip3安装提示权限不足
    将安装器信息下载到目标卷宗失败
    Mac修改默认python版本
    努力吧,少年
    Implement strStr() 字符串匹配
  • 原文地址:https://www.cnblogs.com/Judge/p/10578631.html
Copyright © 2020-2023  润新知