HDU 5726 GCD(RMQ 查询 + 二分)

本文介绍了一种利用区间最大公约数(GCD)特性解决区间查询问题的方法,并通过二分图匹配优化算法,实现了高效求解。文章详细解释了如何通过预处理ST表进行RMQ查询,同时介绍了二分图匹配的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

这里写图片描述

分析

因为gcd有一个特性,区间的公约数等于区间分割后的公约数.所以我门可以预处理一张大的ST表也就是可以用RMQ查询.
然后我们对每一个左端点枚举求出不同gcd的计数(开一个map),暴力的方法就是再枚举右端点。可是由于n太大了,这没法暴力。由于gcd随着区间长度的增长肯定是递减的,所以我们可以二分的来预处理.(O(lgn))最终可以再O(nlgn)预处理出来,然后对每一个询问NlgN

code

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <iostream>
#include <vector>
#include <queue>
#include <map>
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int> Pair;
typedef long long LL;
const int MAX_V  = 30;
const int MAX_E = 1e4+10;
const int maxn = 1e5+10;
//二分图匹配模板
int V;
std::vector<int> G[MAX_V];
bool used[MAX_V];
int match[MAX_V];

bool dfs(int u){
  used[u] = true;
  for(int i=0 ; i<G[u].size() ; ++i){
    int v = G[u][i];int w = match[v];
    if(w<0 || !used[w] && dfs(w)){
      match[u] = v;match[v] = u;
      return true;
    }
  }
  return false;
}

int bipartite_match(){
  int res = 0;
  memset(match,-1,sizeof(match));
  for(int i = 1 ; i<=V ; ++i){
    if(match[i]<0){
      memset(used,false,sizeof(used));
      if(dfs(i))res++;
    }
  }
  return res;
}

void add_edge(int u,int v){
  G[u].push_back(v);
  G[v].push_back(u);
}

int p[20];

int judge[10][10];//是否两个冲突
//建图
void build_graph(int n){
  V = n*2;
  for(int i=1 ; i<=n ; ++i){
    int j = (i == n? 1 :i+1);
    for(int k = 1; k <=n ; ++k){
      if(judge[k][p[i]] || judge[k][p[j]])continue;
      add_edge(i,n+k);
    }
  }
}


int main(int argc, char const *argv[]) {
  int n,m;
  while (scanf("%d%d", &n,&m)!=EOF) {

    for(int i=0 ; i<=n ; ++i)p[i] = i;
    memset(judge,0,sizeof(judge));
    for(int i=0 ; i<m ; ++i){
      int x,y;
      scanf("%d%d",&x,&y );
      judge[x][y] =1;
    }
    if(n == 0){
      std::cout << 0 << '\n';
      continue;
    }

    int ans = 0;
    do{
      for(int i=0 ; i<=20 ; ++i)G[i].clear();
      build_graph(n);
      ans = max(bipartite_match(),ans);
    }while (next_permutation(p+2,p+n+1));//园排列,固定一个端点;
    printf("%d\n",n-ans );
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值