• Codeforces Round #628 (Div. 2)


    传送门:

    https://codeforces.com/contest/1325

    A

    很简单的构造,输出 (1,n-1) 即可。

    int main(){
    	int T; cin>>T;
    	while(T--){
    		 int n; cin>>n;
    		 cout<<1<<' '<<n-1<<endl;
    	}
    	return 0;
    }
    

    B

    需要做一定的转化,发现就是在求数列中有多少不同的值。

    int main(){
    	int T; cin>>T;
    	while(T--){
    		int n; cin>>n;
    		unordered_set<int> st;
    		rep(i,1,n){
    			int v; read(v);
    			st.insert(v);
    		}
    		cout<<(int)st.size()<<endl;
    	}
    	return 0;
    }
    
    

    C

    • 当树退化成链的时候,怎么构造都是一个结果,直接按顺序赋值即可。
    • 如果树不是链,说明存在一个点的度数大于 (3),那我们在这个点上将 (0,1,2) 三个值分配下来即可,这样就能够保证答案为 (2) 了。
    inline void read(int &x) {
        int s=0;x=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-')x=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
        x*=s;
    }
     
    const int N=1e5+50, M=N<<1;
     
    struct Edge{
    	int u, v, w;
    }e[N];
     
    int deg[N];
    bool vis[N];
     
    int main(){
    	int n; cin>>n;
    	rep(i,1,n-1){
    		int u, v; read(u), read(v);
    		e[i]={u, v};
    		deg[u]++, deg[v]++;
    	}
    	
    	int cnt=0;
    	rep(i,1,n) cnt=max(cnt, deg[i]);
    	
    	if(cnt<=2){
    		rep(i,1,n-1) cout<<i-1<<endl;
    		return 0;
    	}
    	
    	int t;
    	rep(i,1,n) if(deg[i]>2){
    		t=i;
    		break;
    	}
    	
    	int cur=0;
    	rep(i,1,n-1){
    		int u=e[i].u, v=e[i].v;
    		if(u==t || v==t && cur<=2) e[i].w=cur++, vis[i]=true;
    	}
    	
    	rep(i,1,n-1) if(!vis[i]) e[i].w=cur++;
    	rep(i,1,n-1) cout<<e[i].w<<endl;
    	
    	return 0;
    }
    

    D

    要求构造一个最短的数列使得异或值为 (u),和为 (v)

    • 首先特判 (0~0) 的情况
    • 如果 (u>v),肯定无解,输出 -1
    • (u=v),一个 (u) 就是答案了。
    • 剩下就是 (u<v) 的情况,(u,v) 奇偶性必须相同,否则无解,因为对于数列异或,其二进制意义下的第一位一定相等,也就是 (u,v) 奇偶性相同。然后在 (u,v) 奇偶性相同的前提下,我们首先发现答案长度一定小于等于 (3),构造方案就是 ({u,frac{v-u}{2},frac{v-u}{2}}),如果 (u &frac{v-u}{2}=0)​,说明二者可以合并,那么此时答案为 (2)
    int main(){
    	ll u, v; cin>>u>>v;
    	if(!u && !v) puts("0");
    	else if(u>v) puts("-1");
    	else if(u==v) cout<<1<<endl<<u<<endl;
    	else if(u<v){
    		ll t=v-u>>1;
    		if((u&1)!=(v&1)) puts("-1");
    		else if(!(u&t)){
    			puts("2");
    			cout<<(u|t)<<' '<<t<<endl;
    		}
    		else{
    			puts("3");
    			cout<<u<<' '<<t<<' '<<t<<endl;
    		}
    	}
    	return 0;
    }
    

    E

    因为每个数 (a_i) 质因子至多两个,故 (a_i) 可以作为边建图,然后边的两点是质因子(或 (1))。

    下面的任务是找到最小环。

    因为大于 (1000) 的点间不存在边,所以以值 (< 1000) 的点为起点多次进行 bfs 找最小环即可。

    对于每次 bfs,队列里的元素是当前点 (u)​ 和它的前驱 (fa)​,

    (u) 展开它所连接的点 (go) 时,

    如果 (go)​ 是 (fa)​ 则跳过,否则看看 (go)​ 是不是 (vis)​ 过的点,不是则入队,是则更新环。

    const int N=1e5+5, V=1e6+5, M=N<<1;
     
    int n;
     
    struct Edge{
    	int to, next;
    }e[M];
     
    int h[N], tot;
     
    void add(int u, int v){
    	e[tot].to=v, e[tot].next=h[u], h[u]=tot++;
    }
     
    int prime[V], cnt;
    bool st[V];
    int id[V];
    void init(){
    	id[1]=1;
    	rep(i,2,V-1){
    		if(!st[i]) prime[++cnt]=i, id[i]=cnt+1;
    		for(int j=1; i*prime[j]<V; j++){
    			st[i*prime[j]]=1;
    			if(i%prime[j]==0) break;
    		}
    	}
    }
     
    bool vis[V];
    vi S;
     
    void upd(int n){
    	int u=0, v=0;
    	for(int i=2; i*i<=n; i++){
    		if(n%i==0){
    			int p=0;
    			while(n%i==0) n/=i, p++;
    			if(p&1){
    				if(!u) u=i;
    				else v=i;
    			}
    		}
    	}
    	if(n>1){
    		if(!u) u=n;
    		else v=n;
    	}
    	
    	if(!u && !v){
    		puts("1");
    		exit(0);
    	}
    	else if(!v){
    		if(!vis[u] && u<=1000) vis[u]=true, S.pb(id[u]);
    		if(!vis[1]) vis[1]=true, S.pb(id[1]);
    		u=id[u], v=id[1];
    		add(u, v), add(v, u);
    	}
    	else{
    		if(!vis[u] && u<=1000) vis[u]=true, S.pb(id[u]);
    		if(!vis[v] && v<=1000) vis[v]=true, S.pb(id[v]);
    		u=id[u], v=id[v];
    		add(u, v), add(v, u);
    	}
    }
     
    struct Node{
    	int u, fa;
    }q[V];
    int tt, hh;
     
    int ans=INF;
     
    int d[N];
     
    int main(){
    	init();
    	cin>>n;
    	
    	memset(h, -1, sizeof h);
    	rep(i,1,n){
    		int v; read(v);
    		upd(v);
    	}
    	
    	for(auto s: S){
    		rep(i,1,cnt) d[i]=INF, vis[i]=false; 
    		tt=-1, hh=0;
    		q[++tt]={s, -1};
    		vis[s]=true;
    		d[s]=0;
    		
    		while(tt>=hh){
    			auto t=q[hh++];
    			int u=t.u, fa=t.fa;
    			for(int i=h[u]; ~i; i=e[i].next){
    				int go=e[i].to;
    				if(go==fa) continue;
    				if(!vis[go]){
    					q[++tt]={go, u};
    					vis[go]=true;
    					d[go]=d[u]+1;
    				}
    				else ans=min(ans, d[u]+d[go]+1);
    			}
    		}
    	}
    	
    	cout<<(ans==INF? -1: ans)<<endl;
    	
    	return 0;
    }
    

    F

    dfs 树。

    我们记 (lim = lceil sqrt n ceil)

    注意到对于一个无向图的边集所对应的 dfs 树的边集只可能是树边回边

    记当前点为 (u)(u) 出发沿着回边走到的点为 (go)

    我们有当 (dep[u]-dep[go]+1geq lim) 时,图上存在大小大于 (lim) 的环。

    假如图不存在大小大于 (lim)​ 的环,那么这意味着对于每个点,其最多有 (lim-2)​​ 条回边(考虑极端情况即得),也就是说你在 dfs 树上选一个点,最多有 (lim-2) 个祖先不能被选取,我们在回溯的过程中做选点操作就可以了。

    复杂度为 (O(N+M))

    // Problem: F. Ehab's Last Theorem
    // Contest: Codeforces - Codeforces Round #628 (Div. 2)
    // URL: https://codeforces.com/contest/1325/problem/F
    // Memory Limit: 256 MB
    // Time Limit: 1000 ms
    // 
    // Powered by CP Editor (https://cpeditor.org)
    
    #pragma GCC optimize("O3")
    #include<bits/stdc++.h>
    using namespace std;
    
    #define endl '
    '
    #define debug(x) cerr << #x << ": " << x << endl
    #define pb push_back
    #define eb emplace_back
    #define set0(a) memset(a,0,sizeof(a))
    #define rep(i,a,b) for(int i=(a);i<=(b);i++)
    #define dwn(i,a,b) for(int i=(a);i>=(b);i--)
    #define ceil(a,b) (a+(b-1))/(b)
    
    #define all(x) (x).begin(), (x).end()
    #define SUM(a) accumulate(all(a), 0LL)
    #define MIN(a) (*min_element(all(a)))
    #define MAX(a) (*max_element(all(a)))
    #define lb(a, x) distance(begin(a), lower_bound(all(a), (x)))
    #define ub(a, x) distance(begin(a), upper_bound(all(a), (x)))
    
    #define INF 0x3f3f3f3f
    #define ll_INF 0x7f7f7f7f7f7f7f7f
    
    using pii = pair<int, int>;
    using pdd = pair<double, double>;
    using vi = vector<int>;
    using vvi = vector<vi>;
    using vb = vector<bool>;
    using vpii = vector<pii>;
    using ll = long long;
    using ull = unsigned long long;
    
    inline void read(int &x) {
        int s=0;x=1;
        char ch=getchar();
        while(ch<'0'||ch>'9') {if(ch=='-')x=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
        x*=s;
    }
    
    const int N=2e5+5, M=N<<2;
    
    struct Edge{
    	int to, next;
    }e[M];
    
    int h[N], tot;
    
    void add(int u, int v){
    	e[tot].to=v, e[tot].next=h[u], h[u]=tot++;
    }
    
    int n, m;
    int lim;
    
    bool vis[N]; // 标记是否作为独立集中的点
    vi res; //  记录独立集
    int d[N]; // 记录深度,同时标记 dfs 中是否访问过该点。
    int stk[N], top; // 维护当前 dfs 栈中的点集。
    
    void dfs(int u){
    	stk[++top]=u;
    	d[u]=top;
    	for(int i=h[u]; ~i; i=e[i].next){
    		int go=e[i].to;
    		if(!d[go]) dfs(go);
    		else if(d[u]-d[go]+1>=lim){
    			puts("2"); cout<<d[u]-d[go]+1<<endl;
    			rep(i,d[go],d[u]) cout<<stk[i]<<' ';
    			exit(0);
    		}
    	}
    	
    	if(!vis[u]){
    		res.pb(u);
    		for(int i=h[u]; ~i; i=e[i].next){
    			int go=e[i].to;
    			vis[go]=true;
    		}
    	}
    	
    	top--;
    }
    
    int main(){
    	memset(h, -1, sizeof h);	
    	cin>>n>>m;
    	rep(i,1,m){
    		int u, v; read(u), read(v);
    		add(u, v), add(v, u);
    	}
    	
    	lim=sqrt(n-1)+1;
    	dfs(1);
    	
    	puts("1");
    	rep(i,0,lim-1) cout<<res[i]<<' ';
    	
    	return 0;
    }
    
  • 相关阅读:
    C# 直接选择排序(史上最清晰,最通俗)
    Hadoop单节点伪分布式环境部署
    Hive安装和部署andMySQL安装和部署
    Kafka集群部署
    HA Hadoop完全分布式环境部署
    HBase集群部署
    Flume整合Kafka
    Hadoop完全分布式环境部署
    JavaScript正则表达式语法
    什么无线路由器性价高?买什么路由器好?
  • 原文地址:https://www.cnblogs.com/Tenshi/p/15431091.html
Copyright © 2020-2023  润新知