zoukankan      html  css  js  c++  java
  • ICPC 北美Mid Central 2019 Regional

    B - Commemorative Race

    • 题意:给一个(n)个点的(DAG),求在最长路上,删除一条边后,从某个入点到达出点的最长路最短是多少。
    • 题解:首先可以先求出每个点到达出点的最长距离和次长距离,然后枚举最长路,在最长路上删边。因为起点不固定,所以建一个超级源点,然后从这个超级源点连向每个点一条边。这就变成了,从起点到出点最长路上删一条边使得删后的最长路最小,求删除后的最长路。可以设一个一维数组(Maxlen)(Maxlen_{i})表示了从(i)到出点的最长路,可以通过每次向下(dfs)返回值然后取(max)就行。并且当(dfs2)模拟沿着最长路走的时候,就只需(Maxlen[u] = Maxlen[v] + 1)看看是否成立,如果成立,那么(v)(u)的下一个的最长路,但是会有多个,如果是个最长路的分叉,那么这里对答案不做贡献,无影响,但是不知道这条分叉的最长路走下去啥情况,不仅要走原来的路,还要走这条最长路的分岔口继续走下去,不过这个分岔口不影响答案。用(ans)数组,记录当前答案,设(dfs2)沿着最长路中走过的距离为(len),那么记录的答案就是之前沿着走过的,加上当前点的除最长路以外的最长的路径长度(Maxlen2)的长度,意味着,当前所记录的结果是,沿着最长路,从起点走到这里的路径长度,加上,从这里走的除了最长路以外的最长的路径长度,我们记录完成,然后顺着(ans)数组取(min)即可.注意,分岔口不能算,(ans)初始是(Maxlen_{0}),即应该是有多条长度一样并且不交叉的最长路,切记初始的时候图是有个超级源点,最后求的是从起点(0)到终点的答案长度,所以我们要求的应该(ans-1).
    • 代码:
    #include <iostream>
    #include <vector>
    using namespace std;
    const int N = 1e5 + 99;
    vector<int>G[N];
    int vis[N];
    int Max_len[N];
    int Max_len2[N]; 
    int cnt[N];
    int ans[N];
    int dfs(int u) {
    	if (vis[u] == 1) {
    		return Max_len[u];
    	}
    	vis[u] = 1;
    	int tmp = 0;
    	for (auto v : G[u]) {
    		tmp =  dfs(v) + 1;
    		if (tmp > Max_len[u]) {
    			Max_len2[u] = Max_len[u];
    			Max_len[u] = tmp;
    		} else if (tmp > Max_len2[u]){
    			Max_len2[u] = tmp;
    		}
    	}
    	return Max_len[u];
    }
    void dfs2(int u, int len) {
    	if (vis[u] == 2) return;
    	vis[u] = 2;
    	for (auto v : G[u])
    	{
    		if (Max_len[u] == Max_len[v] + 1) {
    			dfs2(v, len + 1);
    			cnt[len]++;
    			ans[len] = Max_len2[u] + len;
    		}
    	}
    }
    int main() {
    	int n, m;
    	cin >> n >> m;
    	for (int i = 1; i <= n; i++)G[0].push_back(i);
    	for (int i = 1, u, v; i <= m; i++) {
    		cin >> u >> v;
    		G[u].push_back(v);
    	}
    	dfs(0);
    	dfs2(0, 0);	
    	int Ans = Max_len[0] ;
    	for (int i = 1; i <= n; i++)if (cnt[i] == 1 )  {
    		Ans = min(Ans, ans[i]);
    	}
    	cout << Ans - 1 << endl;
    }
    

    C - Convoy

    • 题意: 给(n)个人,(k)辆车,一辆车最多坐(4)人,然后每个人去目的地花费一定的时间,要求出最少花费的时间。
    • 题解: 一开始乱想,其实发现过程中无法表示,所以应该是二分时间,(check)函数就是,在时间里让前(k)快的司机一直拉人,最后计算一下是否可以把所有人都拉走。
    • 代码:
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const ll N = 200009;
    ll a[N];
    ll n, k;
    bool check(ll mid) {
    	ll cnt = 0;
    	for (ll i = 1; i <= min(k, n); i++) {
    		ll now = mid;
    		if (mid >= a[i]) {
    			cnt += 5;
    			now -= a[i];
    		} else break;
    		ll cost = 2 * a[i];
    		cnt += now / cost * 4;
    		if (cnt >= n)return 1;
    	}
    	return 0;
    }
    signed main() {
    	cin >> n >> k;
    	ll l = 0, r = 0x7fffffffffffffff;
    	for (ll i = 1; i <= n; i++) {
    		cin >> a[i];
    	}
    	sort(a + 1, a + 1 + n); 
    	while (l < r) {
    		ll mid = (l + r) >> 1;
    		if (check(mid)) {
    			r = mid;
    		} else {
    			l = mid + 1;
    		}
    	}
    	cout << l << endl;
    }
    

    F - Dragon Ball I

    • 题意:给出(n)个点, (m)条无向边, (1 <= m,n<= 200 000),然后给出(7)个位置,求从(1)出发依次全部经过这(7)个位置的最短路径。
    • 题解:dij感觉都能想到,并且不能从(1)点贪心的去走,这个也好想,并且还得知道要得有(O(A^{7}_{7}))的常数,dij的复杂度是(O(nlogn)),所以乘起来不大行,想到用数组分别记录从这7个点开始的dij的(dis)数组,并且排列数用next_pumutation很好用。
    • 代码:
    #include <iostream>
    #include <cstring>
    #include <vector>
    #include <queue>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    const ll N = 200009;
    const ll inf = 0x3f3f3f3f3f3f3f3f;
    struct edge {
    	ll v, w;
    };
    vector<edge>G[N];
    struct Node {
    	ll pos,dis;
    	bool operator<(Node rhs)const {
    		return rhs.dis  < dis;
    	}
    };
    ll n, m;
    priority_queue<Node>pq;
    ll d[10][N];
    bool vis[N];
    void dij(ll s, ll rk) {
    	while (!pq.empty()) pq.pop();
    	for (int i = 1; i <= n; i++) {
    		d[rk][i] = inf;
    	}
    	memset(vis, 0, sizeof vis);
    	d[rk][s] = 0;
    	pq.push({s, d[rk][s]});
    	while (!pq.empty()) {
    		auto now = pq.top();
    		pq.pop();
    		ll u = now.pos;
    		if (vis[u])continue;
    		vis[u] = 1;
    		for (auto t : G[u]) {
    			if (d[rk][t.v] > d[rk][u] + t.w) {
    				d[rk][t.v] = d[rk][u] + t.w;
    				pq.push({t.v, d[rk][t.v]});
    			}
    		}
    	}
    }
    ll a[9]{};
    ll ans = 0x7fffffffffffffff;
    void dfs(ll len,ll now, ll pre) {
    	if (len == 7) {
    		ans = min(ans, now);
    		return;
    	}
    	for (ll i = 1; i <= 7; i++) {
    		if (vis[i] == 1) {
    			continue;
    		}
    		vis[i] = 1;
    		dfs(len + 1, now + d[pre][a[i]], i);
    		vis[i] = 0;
    	}
    }
    signed main() {
    	ios::sync_with_stdio(0);
    	cin >> n >> m;
    	for (ll i = 1; i <= m; i++) {
    		ll u, v, w;
    		cin >> u >> v >> w;
    		G[u].push_back({v, w});
    		G[v].push_back({u, w});
    	}
    	a[0] = 1;
    	for (ll i = 1; i <= 7; i++) cin >> a[i];
    	for (ll i = 0; i <= 7; i++) dij(a[i], i);
    	int b[] = {0, 1, 2, 3, 4, 5, 6, 7};
    	do {
    		ll now = 0;
    		for (int i = 1; i <= 7; i++) {
    			now += d[b[i-1]][a[b[i]]];
    			
    		}
    		ans = min(ans, now);
    	} while (next_permutation(b + 1, b + 1 + 7));
    	cout << ans << endl;
    } 
    

    J - Sum and Product

    • 题意:给(n < 2 imes 10^{5})的数量的数,数的大小范围在(int32)内,求一共有多少个连续序列,使得序列数字之和与数字之积相等。
    • 题解:最暴力的办法就是(n ^ {2})暴力跑,但是复杂度在那里必然超时。于是分析得(1)很特殊,先设乘积为(prd), 和为(sum),(1)是唯一一种能延缓乘积增加,并且使(sum)增加的方法。还可发现,除了(1)就算是最小的(2),当连续的(2)用不了多长就会爆,更何况更大的数,所以如果是处理连续的大于(1)的数的话,最差最差是(O(nlog n))处理,发现很客观,那么最烦的(1)咋整?发现,如果遇到(1),可以分析此时(sum)(prd)的性质,发现(prd)是不会变化的,但是(sum)就不一样,它一定是线性(+1)的数量,那么很显然,如果是一段连续的(1),设连续的(1)的数量为(cnt1)要确定是否存在答案,就看是否当前的和加上(1)的数量是否大于乘积的同时,当前和要小于乘积,这样一定在(1)的区间里面对答案造成贡献,即(sum + cnt1 >= prd)并且 (sum < prd),这样两个(for)循环嵌套就可以算完,最差的时间复杂度是(O(2· n ·log n )),就是那种一(2)隔着一个(1).
    • 代码:
    #include <iostream>
    #include <cstring>
    #include <cmath>
    using namespace std;
    const int N = 2e6 + 99;
    typedef long long ll;
    int a[N];
    ll endOne[N];
    ll usum[N];
    signed main() {
    	ll n;
    	ios::sync_with_stdio(0);
    	cin >> n;
    	for (ll i = 1; i <= n; i++) {
    		cin >> a[i];	
    	}
    	for (ll i = n; i >= 1; i--) {
    		usum[i] += usum[i+1] + a[i];
    		if (a[i] == 1) {
    			endOne[i] = max(i, endOne[i+1]);
    		}
    		else endOne[i] = -1;
    	}
    	ll ans = 0;
    	for (ll i = 1;i <= n; i++) {
    		ll prd = a[i];
    		ll sum = a[i];
    		for (ll j = i + 1; j <= n; j++) {
    			if (a[j] == 1) {
    				ll cnt1 = endOne[j] - j + 1;
    				j = endOne[j];
    				if (sum < prd && sum + cnt1 >= prd) {
    					ans++;
    				} 
    				sum += cnt1;
    				continue;
    			}
    			prd *= a[j];
    			sum += a[j];
    			if (sum == prd) {
    				ans++;	
    			}
    			if (usum[i + 1] + sum < prd)break;
    		}
    	}
    	cout << ans << endl;
    }
    

    H - Farming Mars

    • 题意:给(n < 10000)的数量的小数,精确到(6)位,给(m <= n)个询问,询问(l ~ r)区间内相同数的数量的最大值是否不小于 (lfloor frac{(r - l + 1)}{2} + 1 floor)
    • 题解:看到(10000)就想想(n_{2})的暴力,毕竟少了个(0),还是经验不足啊,不敢写。给区间大致排个序然后双指针跑记录数量就行。
    • 代码:
    #include <iostream>
    #include <unordered_map>
    #include <algorithm>
    #include <queue>
    #include <map>
    using namespace std;
    int cnt[15000000];
    const int N = 10009;
    struct node {
    	int l, r, id;
    }q[N];
    bool cmp(node a, node b) {
    	if (a.l == b.l)return a.r < b.r;
    	return a.l < b.l;
    }
    int a[N];
    int ans[N];
    signed main() {
    	int n, m;
    	cin >> n >> m;
    	for (int i = 1; i <= n; i++) {
    		double x;
    		cin >> x;		
    		x *= 1000000;
    		a[i] = x;
    	}
    	for (int i = 1; i <= m; i++) {
    		int l, r;
    		cin >> l >> r;
    		q[i].l = l, q[i].r = r;
    		q[i].id = i;
    	}
    	sort(q + 1, q + 1 + m, cmp);
    	int l = 1, r = 1;
    	cnt[a[l]]++;
    	for (int i = 1; i <= m; i++) {
    		int L = q[i].l;
    		int R = q[i].r;
    		int id = q[i].id;
    		int Max = -1;
    		int cmp = (R - L + 1)/ 2 + 1;
    		while ( l < L) {
    			Max = max(--cnt[a[l++]],Max); 
    		}while (l > L) {
    			Max = max(++cnt[a[--l]],Max); 
    		}while (r > R) {
    			Max = max(--cnt[a[r--]],Max); 
    		} while ( r < R) {
    			Max = max(++cnt[a[++r]],Max); 
    		}
    		Max = -1;
    		for (int i = L; i <= R; i++) {
    			Max = max(Max, cnt[a[i]]);
    		}
    		if (Max >=  cmp) {
    			ans[id] = 1;
    		}
    	}
    	for (int i =1;i  <= m; i++) {
    		if (ans[i])cout << "usable
    ";
    		else cout << "unusable
    ";
    	}
    }
    
  • 相关阅读:
    VS Code安装以及工作区的创建
    var let const的使用和区别
    springboot 配置mysql日期返回格式
    vue安装Node和NPM配置,路由安装。
    分组查询语句(group by函数)
    ORA-00918:未明确定义列
    内连接(inner join)
    右外连接(right join)
    左外连接(left join)
    比较oracle两表中date类型数据是否一致语句查询
  • 原文地址:https://www.cnblogs.com/Xiao-yan/p/14333651.html
Copyright © 2011-2022 走看看