zoukankan      html  css  js  c++  java
  • IOI2021集训队作业 297AK Surveillance

    一个长度为(n)的环,有(k)条弧。选择最少的弧覆盖整个环。

    (n,kle 10^6)


    考虑暴力:枚举第一条弧,然后贪心选下一条,选择左端点小于等于当前弧右端点加一,且右端点最大的弧。一直如此做下去直到覆盖了整个环为止。

    (x)的下一条为(p_x),可以预处理出来。

    网上普遍的做法是用倍增,即枚举每个初始弧,然后倍增出至少要跑多少次才能覆盖整个环。时间(O(klg k))

    我的做法是连边((x,p_x)),在形成的环套树森林中找出每个环,对于每个环任意选择一条初始弧跑。时间(O(k))

    口胡一下其正确性:显然,如果得到了最优的选择弧的集合(S),那么从任意弧(xin S)出发跑,都可以跑出最优解;那么假如从(x)出发跑可以获得最优解,那么从(p_x)出发跑也可以获得最优解;于是可以发现,环上存在最优的初始弧,然后推出环上的每条边都是最优的初始弧。所以任意选一条初始弧即可。


    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 1000005
    int n,k;
    int l[N*2],r[N*2];
    int s[N*2],t[N*2];
    bool cmps(int a,int b){return l[a]<l[b];}
    bool cmpt(int a,int b){return r[a]<r[b];}
    int p[N*2];
    void build(){
    	for (int i=1;i<=k;++i) l[i+k]=l[i]+n,r[i+k]=r[i]+n;
    	for (int i=1;i<=k*2;++i) s[i]=t[i]=i;
    	sort(s+1,s+k*2+1,cmps),sort(t+1,t+k*2+1,cmpt);
    	int mx=0;
    	for (int i=1,j=1;i<=k*2;++i){
    		for (;j<=k*2 && l[s[j]]<=r[t[i]]+1;++j){
    			if (r[s[j]]>r[mx])
    				mx=s[j];
    		}
    		if (r[mx]>r[t[i]])
    			p[t[i]]=mx;
    	}
    	for (int i=1;i<=k;++i){
    		int c=0;
    		if (p[i]==0)
    			c=p[i+k];
    		else if (p[i+k]==0)
    			c=p[i];
    		else
    			c=(r[p[i]]-r[i]>r[p[i+k]]-r[i+k]?p[i]:p[i+k]);
    		if (c>k)
    			c-=k;
    		p[i]=c;
    //		int x=p[i],y=p[i+k];
    //		x=(x>k?x-k:x);
    //		y=(y>k?y-k:y);
    //		if (x==0) p[i]=y;
    //		else if (y==0) p[i]=x;
    //		else p[i]=((r[x]-r[i]+n+n)%n>(r[y]-r[i]+n+n)%n?x:y);
    	}
    }
    int ans;
    int vis[N],cnt,bz[N];
    void dfs(int x){
    	vis[x]=++cnt;
    	bz[x]=1;
    	if (bz[p[x]]){
    		bz[x]=0;
    		int s=1;
    		for (int y=p[x];y!=x;y=p[y]){
    			++s;
    			int tmp=l[x]-1;
    			if (tmp==0)	tmp=n;
    			if (r[y]<n?(l[y]<=tmp && tmp<=r[y]):(tmp<=r[y]-n || tmp>=l[y]))
    				break;
    		}
    		ans=min(ans,s);
    		return;
    	}
    	if (vis[p[x]]){
    		bz[x]=0;
    		return;
    	}
    	dfs(p[x]);
    	bz[x]=0;
    }
    int c[N];
    int main(){
    	freopen("in.txt","r",stdin);
    	freopen("out.txt","w",stdout);
    	scanf("%d%d",&n,&k);
    	for (int i=1;i<=k;++i){
    		scanf("%d%d",&l[i],&r[i]);
    		if (l[i]>r[i]){
    			c[1]++,c[r[i]+1]--;
    			c[l[i]]++;
    			r[i]+=n;
    		}
    		else
    			c[l[i]]++,c[r[i]+1]--;
    		if (r[i]-l[i]+1==n){
    			printf("1
    ");
    			return 0;
    		}
    	}
    	for (int i=1;i<=n;++i){
    		c[i]+=c[i-1];
    		if (!c[i]){
    			printf("impossible
    ");
    			return 0;
    		}
    	}
    	build();
    	ans=k+1;
    	for (int i=1;i<=k;++i){
    		if (!vis[i])
    			dfs(i);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    mac c++编译出现segmentation fault :11错误
    ssh 连接缓慢解决方法
    237. Delete Node in a Linked List
    203. Remove Linked List Elements
    Inversion of Control Containers and the Dependency Injection pattern
    82. Remove Duplicates from Sorted List II
    83. Remove Duplicates from Sorted List
    SxsTrace
    使用CCleaner卸载chrome
    decimal and double ToString problem
  • 原文地址:https://www.cnblogs.com/jz-597/p/13965418.html
Copyright © 2011-2022 走看看