Codeforces Round #686 (Div. 3)

Codeforces Round #686 (Div. 3)

Codeforces Round #686 (Div. 3)

E. Number of Simple Paths

题意:给一颗基环树,问有多少条路径。

题解:通过观察易发现,能经过环到达的点都可以走两条路,而在环上的一颗树上的两点都只有一条路径,所以我们可以算出每个点对都走两条路的答案,再减去树上的点对数即可,首先要找到环,我用dfs类似于求强连通分量的方法求出环上的点,在枚举环上有外接树的点用第二个dfs搜出树的大小,更新答案。

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    ll n,t;
    ll ishuan[200007];
    vector<ll>ho[200007];
    vector<ll>res;
    ll vs;
    ll vis[200007];
    ll fa[200007];
    void init(){
    	vs=0;
    	for(int i=1;i<=n;i++)vis[i]=0;
        for(int i=1;i<=n;i++){
        	ho[i].clear();
        	fa[i]=0;
            ishuan[i]=0;
        }
        res.clear();
    }
    void dfs(ll p,ll f){
    	vis[p]=++vs;
        for(int i=0;i<ho[p].size();i++){
            ll to=ho[p][i];
            if(to==f)continue;
            if(vis[to]){
                if(vis[to]<vis[p])continue;
                res.push_back(to);
                ishuan[to]=1;
                for(;to!=p;to=fa[to]){
                	res.push_back(fa[to]);
                	ishuan[fa[to]]=1;
                }
            }
            else{
            	fa[to]=p;
            	dfs(to,p);
            }
        } 
    }
    ll cal(ll p,ll f){
    	ll sum=1;
    	for(ll i=0;i<ho[p].size();i++){
    		ll to=ho[p][i];
    		if(to==f)continue;
    		sum+=cal(to,p);
    	}
    	return sum;
    }
    int main(){
    	scanf("%lld",&t);
    	while(t--){
    		scanf("%lld",&n);
    	    init();
    	    for(int i=1;i<=n;i++){
    	        int a,b;
    	        cin>>a>>b;
    	        ho[a].push_back(b);
       			ho[b].push_back(a);
    	    }
    	    dfs(1,0);
    	    ll ans=n*(n-1);
    	    for(int i=0;i<res.size();i++){
    	    	int p=res[i];
    	    	ll sum=0;
    	        for(int j=0;j<ho[p].size();j++){
    	        	int to=ho[p][j];
    	        	if(ishuan[to]==0){
    	        		sum+=cal(to,p);
    	        		
    	        	}
    	        }
    	        ans-=sum*(sum+1)/2;
    	    }
    	    printf("%lld
",ans);
    	}
    }
F. Array Partition

题意:给一个区间,分成3个大小不为0的区间,使中间区间的最小值,等于两边区间的最大值。

题解:首先枚举最左边的区间长度,并求出区间最大值x,可知最右边的区间至少要有一个x定下最右边区间的L的最大合法范围,同时我们知道中间区间不能有小于x的值,所以小于x的值要全给最右边的区间,用线段树二分查出最左边的小于x的值的位置于L的最大合法范围取min,作为最右段的L,再用线段树查询区间最值判断是否合法。

    #include<iostream>
    #include<map>
    using namespace std;
    int t,n,now,p,l,r;
    map<int,int>mp;
    struct madoka{
    	int l;
    	int r;
    	int mi;
    	int mx;
    }ma[2000007];
    void build(int l,int r,int k){
    	ma[k].l=l;
    	ma[k].r=r;
    	if(l==r){
    		scanf("%d",&ma[k].mi);
    		mp[ma[k].mi]=l;
    		ma[k].mx=ma[k].mi;
    		return;
    	}
    	int mid=(l+r)/2;
    	build(l,mid,k*2);
    	build(mid+1,r,k*2+1);
    	ma[k].mi=min(ma[k*2].mi,ma[k*2+1].mi);
    	ma[k].mx=max(ma[k*2].mx,ma[k*2+1].mx);
    	return;
    }
    int qry(int k,int l,int r,int z){
    	if(l<=ma[k].l&&ma[k].r<=r){
    		if(z==1){
    			return ma[k].mx;
    		}
    		else{
    			return ma[k].mi;
    		}
    	}
    	int mid=(ma[k].l+ma[k].r)/2;
    	int ans;
    	if(z)ans=0;
    	else ans=1e9;
    	if(mid>=l){
    		if(z)ans=max(qry(k*2,l,r,z),ans);
    		else ans=min(qry(k*2,l,r,z),ans);
    	}
    	if(mid<r){
    		if(z)ans=max(qry(k*2+1,l,r,z),ans);
    		else ans=min(qry(k*2+1,l,r,z),ans);
    	}
    	return ans;
    }
    int fin(int k,int l,int r){
    	if(ma[k].mi>=now){
    		return 1e9;
    	}
    	if(ma[k].l==ma[k].r){
    		return ma[k].l;
    	}
    	if(l<=ma[k].l&&ma[k].r<=r){
    		if(ma[k*2].mi>=now){
    			return fin(k*2+1,l,r);
    		}
    		else{
    			return fin(k*2,l,r);
    		}
    	}
    	int mid=(ma[k].l+ma[k].r)/2;
    	int mi=1e9;
    	if(mid>=l){
    		mi=min(mi,fin(k*2,l,r));
    	}
    	if(mid<r){
    		mi=min(mi,fin(k*2+1,l,r));
    	}
    	return mi;
    }
    int main(){
    	scanf("%d",&t);
    	while(t--){
    		mp.clear();
    		scanf("%d",&n);
    		build(1,n,1);
    		int a=0,b=0,c=0;
    		for(int i=1;i<=n-2;i++){
    			now=qry(1,1,i,1);
    			l=i+2;
    			r=mp[now];
    			if(l>r){
    				continue;
    			}
    			p=fin(1,l,r);
    			if(p==1e9){
    				p=r;
    			}
    			if(qry(1,i+1,p-1,0)==now&&now==qry(1,p,n,1)){
    				a=i,b=p-i-1,c=n-p+1;
    				break;
    			}
    		}
    		if(a+b+c==n){
    			printf("YES
");
    			printf("%d %d %d
",a,b,c);
    		}
    		else{
    			printf("NO
");
    		}
    		
    	}
    }
原文地址:https://www.cnblogs.com/whitelily/p/14036563.html