220906 总结

220906 总结

时间有点短,做到后面稍微有点慌

怎么就想不到应该用什么算法呢。。。

赛时估计:\(100+30+0\)

赛后测试:\(100+30+0\)

赛后一看,还真没难题

T1 购物

直接模拟即可

T2 第 k 小

想的是将所有区间排序,顺着区间统计数量,该区间结束就在下一个区间的这个位置接着跑

但是值域 \(2\times10^9\) 跑不过啊 /youl

做的时候一直在想怎么缩小值域去统计结果(

最后打了个暴力就跑路了

应该但没意识到这个答案具有二分性质,即小于第 \(k\) 小的数都比第 \(k\) 小的数小(什么废话

但是一看题解就发现了。。。呜

\(x\) 表示第 \(k\) 小的数,二分 \(x\)

check 的时候扫一遍所有区间统计小于等于 \(x\) 的数的个数 \(s\)

如果 \(s \ge K\) 则表示 \(x\) 取大了

#include<bits/stdc++.h>
using namespace std;

const int maxN = 1e5;

struct Segment {
	int l, r;
	friend bool operator < (Segment A, Segment B) {
		if (A.l != B.l) return A.l < B.l;
		return A.r > B.r;
	}
} A[maxN];

int N, K;

bool check(int k) {
	int cnt = 0;
	for (int i = 1; i <= N; i++) {
		if (A[i].l <= k) cnt += min(k, A[i].r) - A[i].l + 1;
	}
	return cnt >= K;
}

int main() {
	freopen("thekth.in", "r", stdin);
	freopen("thekth.out", "w", stdout);

	scanf("%d", &N);
	for (int i = 1; i <= N; i++) {
		int l, r;
		scanf("%d%d", &A[i].l, &A[i].r);
	}

	scanf("%d", &K);

	int l = -1e9;
	int r = 1e9;
	int ans = 0;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (check(mid)) {
			ans = mid;
			r = mid - 1;
		} else l = mid + 1;
	}

	printf("%d", ans);
	return 0;
}

T3 最大半连通子图

考的时候在要不要略过 T2 之间摇摆不定导致只剩半小时写这道题了...寄!

半小时糊了一个实际上思路正确但被我写挂了的TLE做法

将所有的 scc 缩成一个点,那么最大半联通子图实际上就是找缩完点形成的这张 DAG 上的最长链

使用拓扑排序更新最长链的长度,如果成功更新,那么重置长度最大值及个数;如果当前长度与最长链长度相同,则更新最长链个数。

Lg 上这么就能过了,但是 Lemon 测的时候还得卡卡常才能过(

#include<bits/stdc++.h>
using namespace std;

const int maxN = 1e5 + 10;
int MOD;

vector<int> G[maxN];

int N, M;

stack<int>s;
int ins[maxN];
int dfn[maxN];
int low[maxN];

int cnt = 0;
int sccNum = 0;
int sccSiz[maxN];
int scc[maxN];

void tarjan(int u) {
	cnt++;
	dfn[u] = cnt;
	low[u] = cnt;
	s.push(u);
	ins[u] = 1;
	for (auto i : G[u]) {
		int v = i;
		if (!dfn[v]) {
			tarjan(v);
			low[u] = min(low[u], low[v]);
		} else if (ins[v]) {
			low[u] = min(low[u], dfn[v]);
		}
	}
	if (low[u] == dfn[u]) {
		ins[u] = 0;
		sccNum++;
		scc[u] = sccNum;
		sccSiz[sccNum] = 1;
		while (s.top() != u) {
			int sT = s.top();
			scc[sT] = sccNum;
			ins[sT] = 0;
			sccSiz[sccNum]++;
			s.pop();
		}
		s.pop();
	}
	return;
}

vector<int> G2[maxN];
int inD[maxN];

inline void reBuid() {
	unordered_set<long long> S;

	for (int i = 1; i <= N; i++) {
		for (auto j : G[i]) {
			long long H = 100000ll * scc[i] + scc[j];
			if (S.count(H)) continue;
			if (scc[i] != scc[j]) {
				G2[scc[i]].push_back(scc[j]);
				inD[scc[j]]++;
			}
			S.insert(H);
		}
	}
}

int dp[maxN];
int num[maxN];

inline void Topo() {
	queue<int> Q;

	for (int i = 1 ; i <= sccNum; i++) {
		if (inD[i] == 0) Q.push(i);
		dp[i] = sccSiz[i];
		num[i] = 1;
	}

	while (!Q.empty()) {
		int u = Q.front();
		Q.pop();
		for (auto i : G2[u]) {
			if (dp[i] < dp[u] + sccSiz[i]) {
				dp[i] = dp[u] + sccSiz[i];
				num[i] = num[u];
			} else if (dp[i] == dp[u] + sccSiz[i]) {
				num[i] = (num[u] % MOD + num[i] % MOD) % MOD;
			}
			if (--inD[i] == 0) Q.push(i);
		}
	}
}

int main() {
	ios :: sync_with_stdio(false);

	freopen("semi.in", "r", stdin);
	freopen("semi.out", "w", stdout);

	cin >> N >> M >> MOD;

	for (int i = 1; i <= M; i++) {
		int u, v;
		cin >> u >> v;
		G[u].push_back(v);
	}
	for (int i = 1; i <= N; i++) if (!dfn[i]) tarjan(i);

	reBuid();
	Topo();

	int dis = -1;
	int ans = -1;

	for (int i = 1; i <= sccNum; i++) {
		if (dp[i] > dis) {
			dis = dp[i];
			ans = num[i];
		} else if (dp[i] == dis) {
			ans = (ans % MOD + num[i] % MOD) % MOD;
		}
	}
	printf("%d\n%d", dis, ans);
	return 0;
}

简单总结

  • 稳一点

  • 觉得算法不行的时候考虑考虑题目具有的性质

  • 别急,急也没用.jpg

  • 码力不太够,表现在代码细节出现令人智熄的离谱错误

posted @ 2022-09-06 22:44  Burnling  阅读(64)  评论(0)    收藏  举报