2024.10.16校测

JPGOJCZX / 2024-10-21 / 原文

T1

题目描述

给你一个无向带权连通图,每条边是黑色或白色。让你求一棵恰好有 \(need\) 条白色边的权值和最小的生成树。题目保证有解。

输入格式

第一行 \(V, E, need\) 分别表示点数,边数和需要的白色边数。

接下来 \(E\) 行,每行 \(s, t, c, col\) 表示这边的端点(点从 \(1\) 开始标号),边权,颜色(\(0\) 白色, \(1\) 黑色)。

输出格式

一行表示所求生成树的边权和。

输入样例

3 3 1
1 2 100 1
1 3 20 0
2 3 25 0

输出样例

120

数据规模

对于 \(20\%\) 数据,\(V, E \leq 20\)

对于另外 \(20\%\) 的数据,\(c \leq 10\)

对于所有数据,\(V, E \leq 100000\)\(c\)\([1, 1000]\) 中的正整数。

完整代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 9;
struct edge{
    int u, v, c, val;
} E[N], tmp[N];
int n, m, need;
int fa[N], sum, cnt;
int find(int x){
	return fa[x] == x ? x : fa[x] = find(fa[x]);
}
bool cmp(edge x, edge y){
	if(x.val == y.val)
		return x.c < y.c;
	return x.val < y.val;
}
bool check(int x){
    sum = 0, cnt = 0;
    for(int i = 1; i <= n; i++)
		fa[i] = i;
    for(int i = 1; i <= m; i++){
        tmp[i] = E[i];
        if(!E[i].c)
			tmp[i].val += x;
    }
    sort(tmp + 1, tmp + m + 1, cmp);
    for(int i = 1; i <= m; i++){
        int fu = find(tmp[i].u), fv = find(tmp[i].v);
        if(fu != fv){
            fa[fu] = fv;
            sum += tmp[i].val;
            cnt += !tmp[i].c;
        }
    }
    return cnt >= need;
}
signed main(){
    freopen("e2.in", "r",stdin);
    freopen("e2.out", "w",stdout);
    scanf("%lld%lld%lld", &n, &m, &need);
    for(int i = 1; i <= m; i++)
    	scanf("%lld%lld%lld%lld", &E[i].u, &E[i].v, &E[i].val, &E[i].c);
    int l = -1005, r = 1005, ans;
    while(l < r){
        int mid = (l + r) / 2;
        if(check(mid)){
        	ans = sum - need * mid;
			l = mid + 1;
		} else
			r = mid;
    }
    printf("%lld", ans);
    return 0;
}

T2

题目描述

\(Q\) 是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏。矩阵游戏在一个 \(N \times N\) 黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作:

  • 行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)

  • 列交换操作:选择矩阵的任意行列,交换这两列(即交换对应格子的颜色)

游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑色。对于某些关卡,小 \(Q\) 百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!!于是小 \(Q\) 决定写一个程序来判断这些关卡是否有解。

输入格式

第一行包含一个整数 \(T\),表示数据的组数。

接下来包含 \(T\) 组数据,每组数据第一行为一个整数 \(N\),表示方阵的大小;接下来 \(N\) 行为一个 \(N * N\)\(01\) 矩阵(\(0\) 表示白色,\(1\) 表示黑色)。

输出格式

输出文件应包含 \(T\) 行。对于每一组数据,如果该关卡有解,输出一行 Yes;否则输出一行 No

输入样例

2
2
0 0
0 1
3
0 0 1
0 1 0
1 0 0

输出样例

No
Yes

数据规模

对于 \(20\%\) 的数据,\(N \leq 6\)

对于 \(100\%\) 的数据,\(N \leq 200, T \leq 10\)

完整代码

#include <bits/stdc++.h>
using namespace std;
const int N = 4e2 + 9;
int G[N][N];
int match[N], reserve_boy[N];
int a[N][N], n, T;
bool dfs(int x){
	for(int i = n + 1; i <= 2 * n; i++)
		if(!reserve_boy[i] && G[x][i]){
			reserve_boy[i] = 1;
			if(!match[i] || dfs(match[i])){
				match[i] = x;
				return true;
			}
		}
	return false;
}
void init(){
	memset(G, 0, sizeof(G));
	memset(match, 0, sizeof(match));
	memset(reserve_boy, 0, sizeof(reserve_boy));
}
int main(){
	freopen("f.in", "r", stdin);
	freopen("f.out", "w", stdout);
	scanf("%d", &T);
	while(T--){
		init();
		scanf("%d", &n);
		for(int i = 1; i <= n; i++)
			for(int j = 1; j <= n; j++){
				scanf("%d", &a[i][j]);
				if(a[i][j] == 1)
					G[i][j + n] = 1;
			}
		int sum = 0;
		for(int i = 1; i <= n; i++){
			memset(reserve_boy, 0, sizeof(reserve_boy));
			if(dfs(i))
				sum++;
		}
		if(sum == n)
			printf("Yes\n");
		else
			printf("No\n");
	}
	return 0;
}

T3

题目描述

有两条蛇(\(1\) 号蛇和 \(2\) 号蛇)在 \(n\)\(m\) 列的地图上,地图上有障碍物。一条蛇碰到蛇身/障碍物/边界就会死。蛇身会不断长长——可以理解为蛇尾位置不会变,蛇只会向前伸展不会缩尾巴。两条蛇都绝顶聪明,如果自己能赢,一定会尽量快地赢;如果自己会输,一定会死得尽量晚。给出初始局面,两蛇轮流走,每次可以且必须向上下左右移动一格。\(1\) 号蛇先走,请告诉我谁会在多少回合时赢。

输入格式

第一行 \(2\) 个整数 \(n, m\),表示地图有 \(n\)\(m\) 列。

接下来 \(n\) 行,每行 \(m\) 个数,表示地图该位置的东西。\(0\) 表示空地,\(1, 2\) 表示两蛇蛇头位置,\(3\) 表示障碍物。(\(1, 2\) 会且只会在 \(1\) 个格子出现)

输出格式

输出一行 \(2\) 个正整数,第一个数表示谁能赢,第二个数表示在第几回合赢。

输入样例

3 3
1 0 0
3 0 3
0 0 2

输出样例

2 5

数据规模

对于 \(50\%\) 数据,\(n \leq 6, m \leq 8\)

对于 \(100\%\) 数据,\(1 \leq n \leq 20, 1 \leq m \leq 20\)\(0\) 的个数不超过 \(50\) 个。

T4

题目描述

\(Alice\)\(Bob\) 经常利用网络传播一些不法信息。网络由 \(n\) 台电脑组成,\(Alice\) 拥有 \(1\) 号电脑,\(Bob\) 拥有 \(n\) 号电脑。有 \(m\) 对电脑间可以通过电缆单向传输信息。为了不被拦截,\(Alice\) 每次传送信息都可以选取任意一条路径(经过的电脑可以重复),把信息沿着电缆传送给 \(Bob\)。现在政府获知了这一情报,他们可以侵入恰好 \(1\) 台电脑,并获取经过这条电脑的所有信息。政府希望(无论 \(Alice\) 选择的路线如何)他们对 \(Alice\) 发送的每条信息都恰好获取 \(1\) 次(如果太少,会漏掉重要情报;如果太多,他们的电脑会被塞满的)。现在你需要求出政府可以选择入侵哪些电脑以达到要求。

输入格式

第一行一个整数 \(t\) 代表测试数据组数。

对于每组数据:第一行两个整数 \(n, m\)。接下来 \(m\) 行,每行两个整数 \(a, b\),表示电脑 \(a\) 可以单向传递信息到电脑 \(b\)。注意 \(a\)\(b\) 可能相同。每对不同的有序对 \((a, b)\) 最多只会出现一次。

输出格式

对于每组数据,先输出一行一个整数 \(k\),代表政府可以选择入侵的电脑数目。接下来一行 \(k\) 个由空格分隔的整数,按照信息从 \(Alice\) 传递到 \(Bob\) 所经过的电脑的顺序,输出政府可以选择入侵的电脑的编号。如果信息不能从 \(Alice\) 传递到 \(Bob\),则认为政府不用入侵任何电脑。

输入样例

4
4 3
2 4
1 3
3 2
2 2
1 2
2 1
3 1
2 3
4 4
1 2
2 4
3 4
1 3

输出样例

4
1 3 2 4
0
0
2
1 4

样例解释

在第二组数据中,由于政府不想看见重复的信息,因此他们无法入侵任何电脑。

在第三组数据中,\(Alice\) 无法将信息传送到 \(Bob\),因此政府不用入侵任何电脑。

在第四组数据中,政府不能入侵电脑 \(2\) 或电脑 \(3\),因为他们可能会漏掉信息。

数据规模

对于 \(30\%\) 数据,\(n \leq 1000, m \leq 3000\)

对于 \(100\%\) 数据,\(2 \leq n \leq 500000, 0 \leq m \leq 1000000, t \leq 10\)

完整代码

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 9, M = 1e6 + 9;
struct Edge{
	int v, nex;
} ori[M << 1], anti[M << 1], dom[M << 1];
int headori[N], headanti[N], headdom[N], oricnt, anticnt, domcnt;
void addori(int u, int v){
	ori[++oricnt] = Edge{v, headori[u]};
	headori[u] = oricnt;
}
void addanti(int u, int v){
	anti[++anticnt] = Edge{v, headanti[u]};
	headanti[u] = anticnt;
}
void adddom(int u, int v){
	dom[++domcnt] = Edge{v, headdom[u]};
	headdom[u] = domcnt;
}
int n, m;
int dfn_tar[N], low[N], ins[N], dfncnt_tar;
int scc[N], sc;
int sz[N];
stack <int> s;
void tarjan(int u) {
	low[u] = dfn_tar[u] = ++dfncnt_tar, ins[u] = 1;
	s.push(u);
	for(int i = headori[u]; i; i = ori[i].nex) {
		const int &v = ori[i].v;
		if(!dfn_tar[v]){
			tarjan(v);
    		low[u] = min(low[u], low[v]);
    	} else if(ins[v])
    		low[u] = min(low[u], dfn_tar[v]);
	}
  	if(dfn_tar[u] == low[u]){
    	++sc;
    	while(s.top() != u){
      		scc[s.top()] = sc;
      		sz[sc]++;
      		ins[s.top()] = 0;
      		s.pop();
    	}
		scc[s.top()] = sc;
		sz[sc]++;
		ins[s.top()] = 0;
		s.pop();
  	}
}
int fat[N], dfn[N], mp[N], dfncnt;
int fa[N], semi[N], idom[N], mid[N], ans;
bool flag[N];
int find(int x){
    if(fa[x] == x)
		return x;
    int fx = fa[x], y = find(fa[x]);
    if(dfn[semi[mid[fx]]] < dfn[semi[mid[x]]])
		mid[x] = mid[fx];
    return fa[x] = y;
}
void dfs(int u, int father){
	dfn[u] = ++dfncnt;
	mp[dfncnt] = u;
	fat[u] = father;
	for(int i = headori[u]; i; i = ori[i].nex){
		int v = ori[i].v;
		if(!dfn[v])
			dfs(v, u);
	}
}
bool vis[N], loop[N];
void calc(int u, int typ){
	vis[u] = true;
	if(flag[u] && sz[scc[u]] == 1 && !loop[u] && typ == 1)
		printf("%d ", u);
	else if(flag[u] && sz[scc[u]] == 1 && !loop[u] && typ == 0)
		ans++;
	for(int i = headori[u]; i; i = ori[i].nex){
		int v = ori[i].v;
		if(vis[v])
			continue;
		calc(v, typ);
	}
}
void Dominate(){
	for(int i = n; i >= 2; i--){
		int tmp = n, u = mp[i];
		for(int j = headanti[u]; j; j = anti[j].nex){
			int v = anti[j].v;
			if(!dfn[v])
				continue;
			if(dfn[v] < dfn[u])
				tmp = min(tmp, dfn[v]);
			else {
				int fv = find(v);
				tmp = min(tmp, dfn[semi[mid[v]]]);
			}
		}
		semi[u] = mp[tmp];
		fa[u] = fat[u];
		adddom(semi[u], u);
		u = mp[i - 1];
		for(int j = headdom[u]; j; j = dom[j].nex){
			int v = dom[j].v;
			int fv = find(v);
			if(semi[mid[v]] == u)
				idom[v] = u;
      		else
			  idom[v] = mid[v];
		}
	}
	for(int i = 2; i <= n; i++){
		int v = mp[i];
		if(idom[v] != semi[v])
      		idom[v] = idom[idom[v]];
	}
	memset(headdom, 0, sizeof(headdom));
	domcnt = 0;
	for(int i = 1; i < n; i++)
		adddom(idom[i], i);
	for(int i = n; ;i = idom[i]){
		if(i == 0)
			break;
		flag[i] = true;
	}
	calc(1, 0);
	memset(vis, 0, sizeof(vis));
	printf("%d\n", ans);
	if(ans == 0){
		printf("\n");
		return;
	}	
	calc(1, 1);
	printf("\n");
}
void init(){
	memset(vis, 0, sizeof(vis));
	memset(fat, 0, sizeof(fat));
	memset(dfn, 0, sizeof(dfn));
	memset(mp, 0, sizeof(mp));
	memset(idom, 0, sizeof(idom));
	memset(flag, 0, sizeof(flag));
	memset(dfn_tar, 0, sizeof(dfn_tar));
	memset(low, 0, sizeof(low));
	memset(ins, 0, sizeof(ins));
	memset(scc, 0, sizeof(scc));
	memset(sz, 0, sizeof(sz));
	memset(headori, 0, sizeof(headori));
	memset(headanti, 0, sizeof(headanti));
	memset(headdom, 0, sizeof(headdom));
	memset(loop, 0, sizeof(loop));
	while(!s.empty())
		s.pop();
	ans = dfncnt = dfncnt_tar = sc = oricnt = anticnt = domcnt = 0;
}
int T;
int main(){
	freopen("i.in", "r", stdin);
	freopen("i.out", "w", stdout);
	scanf("%d", &T);
	while(T--){
		init();
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= n; i++)
			fa[i] = semi[i] = mid[i] = i;
		for(int i = 1; i <= m; i++){
			int u, v;
			scanf("%d%d", &u, &v);
			if(u == v){
				loop[u] = true;
				continue;
			} 
			addori(u, v);
			addanti(v, u);
		}
		tarjan(1);
		if(dfn_tar[n] == 0 || scc[1] == scc[n])
			printf("0\n");
		else {
			dfs(1, 0);
			Dominate();
		}
	}	
	return 0;
}