zoukankan      html  css  js  c++  java
  • 2018北京冬令营测试

    2018北京冬令营测试

    题面戳我!

    Problem A 题解

    我们发现“所有数加起来等于线索上的权值”这个东西可以用流量平衡来解决,所以可以跑上下界费用流。

    建图就是,把上下线索放左边,左右线索放右边,源点向所有上下线索连一个上下界均为对应权值的边,同理左右线索向汇点连一个上下界均为对应权值的边;对于空格就是把左边的一个节点和右边的一个节点连起来,上下界均为这个空格的初始权值。

    然后考虑调整,以上建的每条边都可以调整流量(正向表示权值加,反向表示权值减),注意反向要限制流量为对应初始权值减 (1),这样来保证最终都是正整数。如果代价为 (-1),可以直接不建调整的边。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    #include <map>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 1810
    #define maxm 14420
    #define LL long long
    #define oo 2147483647
    #define ool (1ll << 60)
    
    struct Edge {
    	int from, to, flow, cost;
    	Edge() {}
    	Edge(int _1, int _2, int _3, int _4): from(_1), to(_2), flow(_3), cost(_4) {}
    };
    struct ZKW {
    	int n, m, s, t, head[maxn], nxt[maxm];
    	LL cost, ans;
    	Edge es[maxm];
    	LL d[maxn];
    	int Q[maxn], hd, tl;
    	bool inq[maxn];
    	bool vis[maxn];
    	
    	void init() {
    		m = 0; memset(head, -1, sizeof(head));
    		return ;
    	}
    	void setn(int _) {
    		n = _;
    		return ;
    	}
    	
    	void AddEdge(int a, int b, int c, int d) {
    		es[m] = Edge(a, b, c, d); nxt[m] = head[a]; head[a] = m++;
    		es[m] = Edge(b, a, 0, -d); nxt[m] = head[b]; head[b] = m++;
    		return ;
    	}
    	
    	int Nxt(int x) { return (x + 1) % maxn; }
    	bool BFS() {
    		rep(i, 1, n) d[i] = ool;
    		d[t] = 0;
    		hd = tl = 1; Q[tl = Nxt(tl)] = t; inq[t] = 1;
    		while(hd != tl) {
    			int u = Q[hd = Nxt(hd)]; inq[u] = 0;
    			for(int i = head[u]; i != -1; i = nxt[i]) {
    				Edge& e = es[i^1];
    				if(e.flow && d[e.from] > d[u] + e.cost) {
    					d[e.from] = d[u] + e.cost;
    					if(!inq[e.from]) inq[e.from] = 1, Q[tl = Nxt(tl)] = e.from;
    				}
    			}
    		}
    		if(d[s] == ool) return 0;
    		cost = d[s];
    		return 1;
    	}
    	
    	int DFS(int u, int a) {
    		if(u == t || !a) return ans += cost * a, a;
    		if(vis[u]) return 0;
    		vis[u] = 1;
    		int flow = 0, f;
    		for(int i = head[u]; i != -1; i = nxt[i]) {
    			Edge& e = es[i];
    			if(d[e.to] == d[u] - e.cost && (f = DFS(e.to, min(a, e.flow)))) {
    				flow += f; a -= f;
    				e.flow -= f; es[i^1].flow += f;
    				if(!a) return flow;
    			}
    		}
    		return flow;
    	}
    	
    	int MaxFlow(int _s, int _t) {
    		s = _s; t = _t;
    		int flow = 0, f;
    		while(BFS())
    			do {
    				rep(i, 1, n) vis[i] = 0;
    				f = DFS(s, oo);
    				flow += f;
    			} while(f);
    		return flow;
    	}
    } sol;
    
    #define maxr 35
    
    struct Grid {
    	int tp, v1, v2, c1, c2;
    } gs[maxr][maxr];
    
    int CntP, ind[maxn];
    struct Point {
    	int id;
    	Point(): id(0)  {}
    	int p() { return id ? id : id = ++CntP; }
    } ud[maxr][maxr], lr[maxr][maxr], S, T, SS, TT;
    
    int main() {
    	freopen("kakuro.in", "r", stdin);
    	freopen("kakuro.out", "w", stdout);
    	
    	int n = read(), m = read();
    	rep(i, 1, n) rep(j, 1, m) gs[i][j].tp = read();
    	rep(i, 1, n) rep(j, 1, m) {
    		if(gs[i][j].tp == 1 || gs[i][j].tp == 3) gs[i][j].v1 = read();
    		if(gs[i][j].tp == 2 || gs[i][j].tp == 3) gs[i][j].v2 = read();
    		if(gs[i][j].tp == 4) gs[i][j].v1 = read();
    	}
    	rep(i, 1, n) rep(j, 1, m) {
    		if(gs[i][j].tp == 1 || gs[i][j].tp == 3) gs[i][j].c1 = read();
    		if(gs[i][j].tp == 2 || gs[i][j].tp == 3) gs[i][j].c2 = read();
    		if(gs[i][j].tp == 4) gs[i][j].c1 = read();
    	}
    	
    	sol.init();
    	rep(i, 1, n) rep(j, 1, m) {
    		if(gs[i][j].tp == 1 || gs[i][j].tp == 3) {
    			ind[S.p()] -= gs[i][j].v1;
    			ind[ud[i][j].p()] += gs[i][j].v1;
    			if(gs[i][j].c1 >= 0) {
    				sol.AddEdge(S.p(), ud[i][j].p(), oo, gs[i][j].c1);
    				sol.AddEdge(ud[i][j].p(), S.p(), gs[i][j].v1 - 1, gs[i][j].c1);
    			}
    		}
    		if(gs[i][j].tp == 2 || gs[i][j].tp == 3) {
    			ind[lr[i][j].p()] -= gs[i][j].v2;
    			ind[T.p()] += gs[i][j].v2;
    			if(gs[i][j].c2 >= 0)  {
    				sol.AddEdge(lr[i][j].p(), T.p(), oo, gs[i][j].c2);
    				sol.AddEdge(T.p(), lr[i][j].p(), gs[i][j].v2 - 1, gs[i][j].c2);
    			}
    		}
    		if(gs[i][j].tp == 4) {
    			int x1 = i, y1 = j, x2 = i, y2 = j;
    			while(gs[x1][y1].tp == 4) x1--; // ud
    			while(gs[x2][y2].tp == 4) y2--; // lr
    			ind[ud[x1][y1].p()] -= gs[i][j].v1;
    			ind[lr[x2][y2].p()] += gs[i][j].v1;
    			if(gs[i][j].c1 >= 0) {
    				sol.AddEdge(ud[x1][y1].p(), lr[x2][y2].p(), oo, gs[i][j].c1);
    				sol.AddEdge(lr[x2][y2].p(), ud[x1][y1].p(), gs[i][j].v1 - 1, gs[i][j].c1);
    			}
    		}
    	}
    	sol.AddEdge(T.p(), S.p(), oo, 0);
    	int sum = 0;
    	rep(i, 1, CntP) {
    		if(ind[i] > 0) sol.AddEdge(SS.p(), i, ind[i], 0), sum += ind[i];
    		if(ind[i] < 0) sol.AddEdge(i, TT.p(), -ind[i], 0);
    	}
    	sol.setn(CntP);
    	
    	int flow = sol.MaxFlow(SS.p(), TT.p());
    	if(flow < sum) return puts("-1"), 0;
    	printf("%lld
    ", sol.ans);
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    

    Problem B 题解

    我们将线索看成点,空格就是连接恰好两个点的无向边。把这个图建出来,随便选一棵生成树,非树边就随机一下权值,树边权值就可以确定了(可通过非树边的权值和点权从深往浅递推)。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    #include <map>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    #define LL long long
    
    LL read() {
    	LL x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxr 210
    #define maxn 80010
    #define maxm 160010
    
    LL get_rand() {
    	LL x = 0;
    	rep(i, 0, 11) x = x << 5 | (rand() & (1 << 5) - 1);
    	return x;
    }
    
    int r, c;
    struct Grid {
    	int tp; LL v1, v2;
    } gs[maxr][maxr];
    
    int CntP, eid[maxr][maxr];
    LL val[maxn];
    struct Point {
    	int id;
    	Point(): id(0) {}
    	int p() { return id ? id : id = ++CntP; }
    } ud[maxr][maxr], lr[maxr][maxr];
    
    int M, m, head[maxn], nxt[maxm], to[maxm], id[maxm];
    void AddEdge(int a, int b, int i) {
    	to[++m] = b; id[m] = i; nxt[m] = head[a]; head[a] = m;
    	swap(a, b);
    	to[++m] = b; id[m] = i; nxt[m] = head[a]; head[a] = m;
    	return ;
    }
    
    short vis[maxn], OK;
    LL Ans[maxn];
    void dfs(int u, int fa) {
    	vis[u] = 2;
    	for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
    		if(vis[to[e]] == 2) {
    			Ans[id[e]] = get_rand();
    			val[u] ^= Ans[id[e]]; val[to[e]] ^= Ans[id[e]];
    		}
    		else if(!vis[to[e]]) {
    			dfs(to[e], u); Ans[id[e]] = val[to[e]];
    			val[u] ^= Ans[id[e]]; val[to[e]] ^= Ans[id[e]];
    			if(val[to[e]]) OK = 0;
    		}
    	}
    	if(!fa && val[u]) OK = 0;
    	vis[u] = 1;
    	return ;
    }
    
    map <LL, bool> has;
    void work() {
    	rep(i, 1, r) rep(j, 1, c) ud[i][j].id = lr[i][j].id = 0;
    	r = read(); c = read();
    	rep(i, 1, r) rep(j, 1, c) gs[i][j].tp = read();
    	rep(i, 1, r) rep(j, 1, c) {
    		if(gs[i][j].tp == 1 || gs[i][j].tp == 3) gs[i][j].v1 = read();
    		if(gs[i][j].tp == 2 || gs[i][j].tp == 3) gs[i][j].v2 = read();
    	}
    	CntP = M = m = 0; memset(head, 0, sizeof(head));
    	rep(i, 1, r) rep(j, 1, c) if(gs[i][j].tp == 4) {
    		int x1 = i, y1 = j, x2 = i, y2 = j;
    		while(gs[x1][y1].tp == 4) x1--; // ud
    		while(gs[x2][y2].tp == 4) y2--; // lr
    		val[ud[x1][y1].p()] = gs[x1][y1].v1;
    		val[lr[x2][y2].p()] = gs[x2][y2].v2;
    		AddEdge(ud[x1][y1].p(), lr[x2][y2].p(), eid[i][j] = ++M);
    	}
    	memset(vis, 0, sizeof(vis)); OK = 1;
    	rep(i, 1, CntP) if(!vis[i]) {
    		dfs(i, 0);
    		if(!OK) return (void)puts("-1");
    	}
    	has.clear();
    	rep(i, 1, M) {
    		if(!Ans[i] || has.count(Ans[i])) return (void)puts("-1");
    		has[Ans[i]] = 1;
    	}
    	rep(i, 1, r) {
    		int lst = 0;
    		rep(j, 1, c) if(gs[i][j].tp == 4) lst = max(lst, j);
    		rep(j, 1, c) if(gs[i][j].tp == 4) printf("%lld%c", Ans[eid[i][j]], j < lst ? ' ' : '
    ');
    	}
    	return ;
    }
    
    int main() {
    	freopen("cross.in", "r", stdin);
    	freopen("cross.out", "w", stdout);
    	
    	int T = read();
    	
    	while(T--) work();
    	
    	fclose(stdin);
    	fclose(stdout);
    	
    	return 0;
    }
    

    Problem C 题解

    这个每条信息的候选数都是一段连续的区间最多去掉一个数字【需要分情况讨论,当 (len le 2)(len) 表示线索限制的区间长度)时候选数字是单点】。于是我们可以用一个扫描线从上往下扫描这个网格,遇到一个纵向的线索的开头就加入数据结构,结尾就从数据结构删除,然后对于一个横向的线索相当于区间查询信息。我们开一个线段树套线段树,外层位置内层值域,维护左右端点的总和(如果候选数字是贴着 (k) 的区间就存左端点,是贴着 (1) 的区间就存右断电)、删除的单点,以及如果候选数字是单点就维护一下这个单点信息。

    具体实现我也不会,就是不知道如何求出删掉哪个值,感觉这种分类讨论题非常恶心

  • 相关阅读:
    整除
    奇怪的生日礼物
    欧拉函数平方和
    奇怪的生日礼物(数论基础)
    整除(简单数论)
    Tarjan求割点
    构造双连通(tarjan)
    次小生成树
    机器扫边
    最短路径(树形DP)
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8372319.html
Copyright © 2011-2022 走看看