• 2018.08.21高二互测


    2018.08.20 NOIp模拟赛

    redbag的神题,真的很思维啊。。真的很工业啊。。

    本博客大量借用redbag的博客,侵删。

    第一题

    ​ 给你\(~n~\)个数对,要求对于每个数对取出一个数计入答案,所有另一个数组成的集合大小为\(~n~\), 最大化答案并输出, 保证数据合法。\(~1 \leq n \leq 2.5 \times 10^5~, ~1 \leq x_i, y_i \leq 10 ^ 9\)

    ​ 离散化数对之后对每个点对的 \(~x, ~y~\)之间连一条边,可以发现对于任意一条边都有一端作为贡献且另一端作为限制,考虑对边定向,\(~x~\)指向\(~y~\)表示\(~y~\)计入贡献且\(~x~\)为限制。因为数据合法,可以发现图是一个由__树__和__基环外套树__组成的森林。那么遍历每一个联通块:若当前是一颗树,则可以选择一个权值最大的点贡献\(~deg_u~\)次,其他所有点贡献\(~deg_u - 1~\)次;而对于基环外套树,因为边数和点数相等,所以每一个点都只能贡献\(~deg_u - 1~\)次。

    code

    #include<bits/stdc++.h>
    #define x first
    #define y second
    #define mp make_pair
    #define For(i, j, k) for(int i = j; i <= k; ++i)
    #define Forr(i, j, k) for(int i = j; i >= k; --i)
    #define Travel(i, u) for(int i = beg[u], v = to[i]; i; i = nex[i], v = to[i])
    using namespace std;
    
    inline int read() {
    	int x = 0, p = 1; char c = getchar();
    	for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
    	for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    	return x *= p;
    }
    
    template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
    template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
    
    inline void File() {
    	freopen("s.in", "r", stdin);
    	freopen("s.out", "w", stdout);
    }
    
    typedef long long ll;
    typedef pair<int, int> PII;
    const int N = 25e4 + 1e2, M = N << 1;
    int n, tot, ls[M], deg[M], siz[M], t, cnt, vis[M];
    int e = 1, beg[M], nex[M << 1], to[M << 1];
    vector<int> vec[M]; PII P[N];
    
    inline void add(int x, int y) {
    	to[++ e] = y, nex[e] = beg[x], beg[x] = e, ++ deg[y];
    	to[++ e] = x, nex[e] = beg[y], beg[y] = e, ++ deg[x];
    }
    
    inline void dfs(int u, int f, int tag) {
    	vis[u] = tag, vec[tag].push_back(u), t += deg[u];
    	Travel(i, u) if (v != f && !vis[v]) dfs(v, u, tag);
    }
    
    int main() {
    	File();
    	n = read();
    	For(i, 1, n) {
    		P[i].x = read(), P[i].y = read();
    		ls[++ tot] = P[i].x, ls[++ tot] = P[i].y;	
    	}
    	sort(ls + 1, ls + 1 + tot), tot = unique(ls + 1, ls + 1 + tot) - ls - 1;
    	For(i, 1, n) {
    		P[i].x = lower_bound(ls + 1, ls + 1 + tot, P[i].x) - ls;
    		P[i].y = lower_bound(ls + 1, ls + 1 + tot, P[i].y) - ls;
    		add(P[i].x, P[i].y);
    	}
    
    	For(i, 1, n) if (!vis[i]) 
    		++ cnt, t = 0, dfs(i, 0, cnt), siz[cnt] = t >> 1;
    
    	ll ans = 0;
    	For(i, 1, cnt) {
    		int sz = vec[i].size(); 
    		if (sz == siz[i]) 
    			for (auto v : vec[i]) ans += 1ll * (deg[v] - 1) * ls[v];
    		else {
    			int res = 0;
    			for (auto v : vec[i]) ans += 1ll * (deg[v] - 1) * ls[v], res = max(res, ls[v]);
    			ans += res;
    		} 
    	}
    
    	printf("%lld\n", ans);
    	return 0;
    }
    
    

    第二题

    ​ 数据规模\(~O(n ^ 2)~\)还可以套一个小\(~log~\),超级背包四合一,第一二问都可以直接\(~dp~\)。对于第三问,可以考虑把序列按一个奇怪的法则排序,按照作业本的课时数将作业本分成若干类。依次从小到大在每一类中取一本作业本,加入到数组里,直到所有的作业被取完,这样再跑背包,枚举第一符合条件的位置,输出其在之前出现过的次数就行了,而要完成这样的排序,可以用简单暴力的\(~multiset~\),也可以用常数小点的链表。对于第四问,题解太神了。

    code

    #include<bits/stdc++.h>
    #define Set(a, b) memset(a, b, sizeof (a))
    #define For(i, j, k) for(int i = j; i <= k; ++i)
    #define Forr(i, j, k) for(int i = j; i >= k; --i)
    using namespace std;
    
    inline int read() {
    	int x = 0, p = 1; char c = getchar();
    	for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
    	for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    	return x *= p;
    }
    
    template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
    template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
    
    inline void File() {
    	freopen("b.in", "r", stdin);
    	freopen("b.out", "w", stdout);
    }
    
    const int N = 5e3 + 10, inf = 0x3f3f3f3f;
    int n, V, a[N], minn;
    
    namespace Task1 {
    	int dp[N];
    
    	inline void Solve() {
    		Set(dp, 127), dp[0] = 0;
    		For(i, 1, n) Forr(j, V, a[i]) chkmin(dp[j], dp[j - a[i]] + 1);
    		minn = dp[V], printf("%.7lf ", 1.0 * V / minn);
    	}
    }
    
    namespace Task2 {
    	
    	int dp1[N][N], dp2[N][N];
    
    	inline void Solve() {
    		Set(dp1, 127), Set(dp2, 127); dp1[0][0] = 0, dp2[n + 1][0] = 0;	
    	
    		For(i, 1, n) Forr(j, V, 0) {
    			if (j >= a[i]) chkmin(dp1[i][j], dp1[i - 1][j - a[i]] + 1);
    			chkmin(dp1[i][j], dp1[i - 1][j]);
    		}
    
    		Forr(i, n, 1) Forr(j, V, 0) {
    			if (j >= a[i]) chkmin(dp2[i][j], dp2[i + 1][j - a[i]] + 1);
    			chkmin(dp2[i][j], dp2[i + 1][j]);
    		}
    
    		int res = minn + 1 >> 1;
    		For(i, 1, n) For(j, 0, V) {
    			if (dp1[i][j] + dp2[i + 1][V - j] == minn && dp1[i][j] == res) {
    				printf("%d ", a[i]); return;
    			}
    		}
    	}
    }
    
    namespace Task3 {
    	int dp[N][N], tmp[N], cnt[N], pre[N], nex[N], A[N];
    
    	inline void Solve() {
    		For(i, 1, n) ++ cnt[a[i]], A[i] = a[i];
    	
    		int tot = unique(A + 1, A + 1 + n) - A - 1;
    		For(i, 1, tot) {	
    			pre[A[i]] = i == 1 ? A[tot] : A[i - 1];
    			nex[A[i]] = i == tot ? A[1] : A[i + 1];
    		}
    		int c = 0, now = A[1];
    		for (;;) {
    			if (c == n) break; 
    			tmp[++ c] = now, -- cnt[now];
    			if (!cnt[now]) nex[pre[now]] = nex[now], pre[nex[now]] = pre[now];
    			now = nex[now];
    		}	
    
    		Set(dp, 127), dp[0][0] = 0;
    		For(i, 1, n) Forr(j, V, 0) {
    			if (j >= tmp[i]) chkmin(dp[i][j], dp[i - 1][j - tmp[i]] + 1);
    			chkmin(dp[i][j], dp[i - 1][j]);
    		}
    
    		For(i, 1, n) {
    			++ cnt[tmp[i]];
    			if (dp[i][V] == minn) { printf("%d ", cnt[tmp[i]]); return; }
    		}
    	}
    }
    
    namespace Task4 {
    
    	int dp1[N][N], dp2[N][N], S1[N], S2[N];
    	int c1, c2, ans = inf, l = 1, r = 0;
    
    	inline bool check() {
    		For(i, 0, V) if (dp1[c1][i] + dp2[c2][V - i] == minn) return true;
    		return false;
    	}
    
    	inline void clear1() { Set(dp1, 127), dp1[0][0] = 0; }
    	inline void clear2() { Set(dp2, 127), dp2[0][0] = 0; }
    
    	inline void Solve() {
    		clear1(), clear2();
    		while (l <= n && r <= n) {
    			if (check()) {
    				chkmin(ans, a[r] - a[l]), ++ l;			
    				if (!c1) {
    					clear1();
    					Forr(i, c2, 1) {
    						S1[++ c1] = S2[i];
    						For(st, 0, V) dp1[c1][st] = dp1[c1 - 1][st];
    						Forr(j, V, S1[c1]) chkmin(dp1[c1][j], dp1[c1 - 1][j - S1[c1]] + 1);
    					}
    					c2 = 0, clear2();
    				} -- c1;		
    			} else {
    				++ r, S2[++ c2] = a[r];
    				For(st, 0, V) dp2[c2][st] = dp2[c2 - 1][st];
    				Forr(j, V, a[r]) chkmin(dp2[c2][j], dp2[c2 - 1][j - a[r]] + 1);
    			}
    		}
    
    		printf("%d\n", ans);
    	}
    }
    
    int main() {
    	File();
    	n = read(), V = read();
    	For(i, 1, n) a[i] = read();
    	sort(a + 1, a + 1 + n);
    
    	Task1::Solve();
    	Task2::Solve();
    	Task3::Solve();
    	Task4::Solve();
    	return 0;	
    }
    
    

    第三题

    ​ 给你一张\(~n~\)个点\(~m~\)条边的带权无向图,对于一条合法路径,你可以不用支付最贵的\(~k~\)条路,求最短路。\(~1 \leq n, m \leq 3000, ~0 \leq k \leq m, z \leq 10 ^ 9~\)。考虑枚举每一条边的权值\(~w_i~\),使所有边变成\(~max \{0, ~T_i - w_i\}~\), 跑最短路,最后的答案加上\(~k \times T_i~\),取最小值。至于这样为什么是对的,脑补一下一些情况就可以知道了。这个方法很套路,考场上根本想不到。。。

    code

    #include<bits/stdc++.h>
    #define fir first
    #define sec second
    #define mp make_pair
    #define For(i, j, k) for(int i = j; i <= k; ++i)
    #define Forr(i, j, k) for(int i = j; i >= k; --i)
    #define Travel(i, u) for(int i = beg[u], v = to[i]; i; i = nex[i], v = to[i])
    using namespace std;
    
    inline int read() {
    	int x = 0, p = 1; char c = getchar();
    	for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
    	for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    	return x *= p;
    }
    
    inline void File() {
    	freopen("y.in", "r", stdin);
    	freopen("y.out", "w", stdout);
    }
    
    typedef long long ll;
    typedef pair<ll, int> PII;
    const int N = 3e3 + 10, M = N << 1;
    const ll inf = 1e15;
    int e = 1, beg[N], nex[M], to[M], vis[N], n, m, k, tot;
    ll dis[N], w[M], ans, T[M], ls[M];
    
    inline void add(int x, int y, ll z) {
    	to[++ e] = y, nex[e] = beg[x], beg[x] = e, T[e] = z;
    	to[++ e] = x, nex[e] = beg[y], beg[y] = e, T[e] = z;
    }
    
    inline ll dij(ll x) {
        priority_queue<PII, vector<PII>, greater<PII> > Q;
    	For(i, 2, e) w[i] = max(0ll, T[i] - x);
    	For(i, 1, n) vis[i] = 0, dis[i] = inf;
    	dis[1] = 0, Q.push(mp(0, 1));
    
        while (!Q.empty()) {
            PII x = Q.top(); int u = x.sec; Q.pop();
            if (!vis[u]) {
                vis[u] = 1;
                Travel(i, u) if(dis[v] > x.fir + w[i]) {
                    dis[v] = x.fir + w[i];
                    Q.push(mp(dis[v], v));
                }
            }
        }
    	return dis[n] + k * x;
    }
    
    int main() {
    	File();
    	cin >> n >> m >> k;
    	For(i, 1, m) {
    		int x = read(), y = read(), z = read();
    		add(x, y, 1ll * z), ls[++ tot] = z;
    	}	
    	sort(ls + 1, ls + 1 + tot), tot = unique(ls + 1, ls + 1 + tot) - ls - 1;
    
    	ll ans = dij(0);
    	For(i, 1, tot) ans = min(ans, dij(ls[i]));
    	printf("%lld\n", ans);
    	return 0;
    }
    
    

    ​ 至于今天的题目名字,连起来就是一个很美丽的名字了。

  • 相关阅读:
    A Color Game
    《算法分析》作业1
    The 2018 ACM-ICPC CCPC NING XIA G-Factories
    2017北京ICPC Pangu and Stones(区间DP)
    Comet OJ
    2019牛客多校训练营第四场补题
    2019牛客多校训练营第五场补题
    2017CCPC哈尔滨 B:K-th Number(二分+尺取)
    启发式分治入门 Non-boring sequences UVA
    2019牛客多校训练营第三场补题
  • 原文地址:https://www.cnblogs.com/LSTete/p/9515575.html
Copyright © 2020-2023  润新知