「模拟赛」暑期集训CSP提高模拟11(7.29)

54ptsRank29

总结写前面:

  • T1 掌握 O(n) 求次短路长度及个数;

  • T2、T3 新思路,都很巧妙;

  • T4 珂朵莉树

A. Fate

评价:数据过水。

赛后听说纯暴力搜索就能过,我交了个暴力代码,100pts,不是啊这我要疯了,赛时算的时间复杂度太炸了于是这么写的( n<100 时爆搜,其余随机骗分,拿了 19pts),这我不亏大发了。

之后丁真造出了 hack 数据,但只卡到了 99pts,怎么这次学长那么仁慈了呢。

赛时小丑代码
#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
using namespace std;

const int N = 2e5 + 10;
const int mod = 1e9 + 7;

int n, m, s, t, sr, f[N][2];

int head[N], to[N<<1], nxt[N<<1], tot;
void add(int x, int y){
	to[++tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
}	

int dis[N], vis[N], di[N];
void Dij(int a){
	memset(dis, 0x3f3f3f3f, sizeof dis);
	memset(vis, false, sizeof vis);
	priority_queue<pair<int, int> >q;

	dis[a] = 0;
	q.push(mp(-dis[a], a));

	while(q.size())
	{
		int x = q.top().second;
		q.pop(), vis[x] = 1;

		for(int i=head[x]; i; i=nxt[i]){
			int y = to[i];
			if(dis[y] > dis[x] + 1){
				dis[y] = dis[x] + 1;

				if(!vis[y]) q.push(mp(-dis[y], y));
			}
		}
	}
}

bool fl[N];
int dfs(int now, int p, int len){
	if(now == t and len == dis[t] + 1) return 1;
	if(now == t) return 0;
	if(len == dis[t] + 1) return 0;

	ll ans = 0;
	for(int i=head[now]; i; i=nxt[i]){
		int y = to[i];
		if(fl[y]) continue;
		fl[y] = true;
		ans = (ans + dfs(y, now, len+1)) % mod;
		fl[y] = false;
	}
	return ans;
}

void Dij_Aqr(int a){
	memset(di, 0x3f3f3f3f, sizeof di);
	memset(vis, false, sizeof vis);
	priority_queue<pair<int, int> >q;

	di[a] = 0;
	q.push(mp(-di[a], a));

	while(q.size())
	{
		int x = q.top().second;
		q.pop(), vis[x] = 1;

		for(int i=head[x]; i; i=nxt[i]){
			int y = to[i];
			if(di[x] + 1 == dis[y] + 1) f[y][1]++;
			if(di[y] > di[x] + 1){
				di[y] = di[x] + 1;
			}
			if(!vis[y]) q.push(mp(-di[y], y));
		}
	}
}

int main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

	cin>>n>>m>>s>>t;

	for(int i=1; i<=m; i++){
		int x, y; cin>>x>>y;
		add(x, y), add(y, x);
	}

	Dij(s); sr = dis[t];

	if(n <= 100){
		fl[s] = true;
		cout<<dfs(s, 0, 0);
		return 0;
	}

	Dij_Aqr(s);

	cout<<f[t][1];

	return 0;
}

题意:

XXXXX

正解:

显然我们需要跑一边 Dij 求最短路径长度,我们可以在求最短路的同时维护出次短路的长度及个数,那么我们最后特判一下次短路长度是否为最短路长度 +1,是输出次短路个数,不是输出 0。

code:

#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
using namespace std;

const int N = 4e5 + 10;
const int mod = 1e9 + 7;

int n, m, s, t, sr, dis[N][2], cnt[N][2], vis[N][2];

int head[N], to[N<<1], nxt[N<<1], tot;
void add(int x, int y){
	to[++tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
}	

void Dij_Aqr(int a){
	memset(dis, 0x3f, sizeof dis);
	memset(vis, false, sizeof vis);
	priority_queue<pair<int, pair<int, int>> >q;

	dis[a][0] = 0, cnt[a][0] = 1;
	q.push(mp(0, mp(a, 0)));

	while(q.size())
	{
		int x = q.top().second.first, tag = q.top().second.second;
		q.pop(); 
		if(vis[x][tag]) continue;
		vis[x][tag] = 1;

		for(int i=head[x]; i; i=nxt[i]){
			int y = to[i];
			if(dis[y][0] > dis[x][tag] + 1){
				dis[y][1] = dis[y][0];
				dis[y][0] = dis[x][tag] + 1;
				cnt[y][1] = cnt[y][0];
				cnt[y][0] = cnt[x][tag];

				q.push(mp(-dis[y][1], mp(y, 1)));
				q.push(mp(-dis[y][0], mp(y, 0)));
			}		
			else if(dis[y][0] == dis[x][tag] + 1) cnt[y][0] = (cnt[y][0] + cnt[x][tag]) % mod;
			else if(dis[y][1] > dis[x][tag] + 1){
				dis[y][1] = dis[x][tag] + 1;
				cnt[y][1] = cnt[x][tag];
				q.push(mp(-dis[y][1], mp(y, 1)));
			}
			else if(dis[y][1] == dis[x][tag] + 1) cnt[y][1] = (cnt[y][1] + cnt[x][tag]) % mod;
			
		}
	}
}

int main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

	cin>>n>>m>>s>>t;

	for(int i=1; i<=m; i++){
		int x, y; cin>>x>>y;
		add(x, y), add(y, x);
	}

	Dij_Aqr(s);

	if(dis[t][1] == dis[t][0] + 1)
		cout<<cnt[t][1];
	else cout<<0;


	return 0;
}

B.EVA 45 pts

赛时打的 n3 暴力,已经跟答案很接近了,拿了暴力 45pts

题意:

总共有 n 条鱼,所有鱼在一条数轴上运动,第 i 条鱼价值为 wi,在时刻 0 时在位置 xi ,将会以每时刻 vi 的速度向数轴正方向移动。

可以选择一个时刻 t 在位置 x 撒下一个长度为 a 的网,此时所有处于 [x,x+a] 的鱼都会被捕获。(此处选择的 xt 不必是整数)。求出撒一次网能捕获的鱼的价值和的最大值。

正解:

枚举每一条鱼为网的左端点(网的左端点在一条鱼上一定最不劣),计算你其他所有鱼进入和出去这个网的时间,按升序排列,然后按时间顺序遍历每一个事件,如果是有鱼进入网内,则加上这条鱼的贡献,有鱼离开网的范围则减去该鱼的贡献。(计算鱼进出的时间可以用相对运动,将作网左端点的鱼视为静止,我是根据式子直接计算)。

code:

#include<bits/stdc++.h>
#define x(i) a[i].x
#define w(i) a[i].w
#define v(i) a[i].v
#define mp make_pair
using namespace std;

const int N = 4e3 + 10;
const double MAX = 2e9;

int n, len;

struct fish{
	double x; int v, w;
}a[N];

struct area{
	double shu; int wa, f;

	bool operator < (const area &A) const{
		if(shu == A.shu){
			return f > A.f;
		}
		return shu < A.shu;
	}
}t[N];

pair<double, double> solve(int i, int j){
	if(x(j) < x(i) and v(j) <= v(i)) return mp(1.0, 0.0);
	if(x(i) < x(j) - len and v(i) <= v(j)) return mp(1.0, 0.0);

	double l = 0, r = 0;
	if(x(i) < x(j)){
		if(v(i) > v(j)){
			r = (x(j) - x(i)) / (v(i) - v(j));
			l = max(0.0, ( (len + x(i) - x(j)) / (v(j) - v(i)) ) );
			
			return mp(l, r);
		}
		if(v(i) == v(j)) return mp(0, MAX);
		if(v(i) < v(j)){
			r = (len + x(i) - x(j)) / (v(j) - v(i));
			return mp(0, r);
		}
	}
	else{
		l = (x(i) - x(j)) / (v(j) - v(i));
		r = (len + x(i) - x(j)) / (v(j) - v(i));
		return mp(l, r);
	}
	return mp(0.0, 0.0);
}

int main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

	cin>>n>>len; int ans = 0;

	for(int i=1; i<=n; i++){
		cin>>w(i)>>x(i)>>v(i);
		ans = max(ans, w(i));
	}


	for(int i=1; i<=n; i++){ // left_fish
		memset(t, 0, sizeof t); int cnt = 0;
		for(int j=1; j<=n; j++){
			if(i == j) continue;

			double l = 0, r = 0;
			if(x(i) == x(j) and v(i) == v(j)) l = 0, r = MAX;
			else{
				pair<double, double> now = solve(i, j);
				if(now.first > now.second) continue;
				l = now.first, r = now.second;
			}
			t[++cnt] = {l, w(j), 1};
			t[++cnt] = {r, w(j), -1};
		}

		sort(t+1, t+1+cnt); int hnt = w(i);
		for(int j=1; j<=cnt; j++){
			hnt += t[j].wa * t[j].f;
			ans = max(ans, hnt);
		}
	}
	
	cout<<ans;

	return 0;
}

C.嘉然登场

原题 ARC148E

思路很巧妙的题!值得以后复习。

赛时大致思路很接近,我已经可以往正解的方向去想了,但是具体如何死活想不出来,还是见得少,没有解决这种题型的经验。

数据中有 20pts 的 0 和 1 序列,在引导正解,想到个大概了,但是有些东西确实不会,(像情况数的处理)。

正解:

根据 0、1 数据的引导,我们容易想到将序列分为 <k/2k/2 的两部分;

然后处理每一个小于 k/2 的数,设处理的数为 a,从 k/2 的部分里找到最小的 b 使得 a+bk,两者挂钩。

接着从大到小遍历 k 部分中的数,(初始化 T=1,表示现在可插入的位置数,最开始只有一个),每次遍历到一个新的数,答案乘上当前的 T,遇到一个新的 k 的数,T++;,并处理所有与这个数挂钩的数,挂钩的数肯定是 <k 的,则每处理一个,答案同样乘上当前的 T,但是 T--;

最后因为可能会有相同的数,于是需要去重:答案除去每个相同的数的个数的阶乘。

code:

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define mp make_pair
using namespace std;

const int N = 2e5 + 10;
const int mod = 998244353;

int n, k, K, a[N], jie[N];
map<int, int>kind;
map<int, int>to;

int qpow(ll A){
	int b = mod - 2; ll res = 1;
	while(b)
	{
		if(b & 1) res = res * A % mod;
		A = A * A % mod;
		b >>= 1;
	}
	return res;
}

signed main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

	cin>>n>>K; k = (K + 1) / 2;
	for(int i=1; i<=n; i++) cin>>a[i], kind[a[i]]++;
	sort(a+1, a+1+n);

	for(int i=1; i<=n; i++){
		if(a[i] >= k) break;
		int y = lower_bound(a+1, a+1+n, K-a[i]) - a;
		to[y]++;
	}

	int T = 1; ll ans = 1;
	for(int i=n; i>=1; i--){
		if(a[i] >= k){
			// cout<<a[i]<<" "<<to[a[i]]<<"\n";
			ans = ans * T % mod, T++;
			for(int j=1; j<=to[i]; j++){
				ans = ans * T % mod, T--;
			}
		}
		else break;
	}


	ll jie = 1;
	for(int i=1; i<=n; i++){
		for(int j=1; j<=kind[a[i]]; j++){
			jie = jie * j % mod;
		}
		kind[a[i]] = 0;
	}

	cout<<ans*qpow(jie)%mod;

	return 0;
}

D.Clannad

原题:洛谷P9340

题意:

略...

正解:

两种做法,不细讲了(因为贺的题解,没那么懂)

  1. 珂朵莉树+树状数组+lca

  2. 回滚莫队

放一下珂朵莉树代码。

code:

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

const int N = 1e5 + 10;

int n, m, Q;
int a[N], st[N][20], ans[N];
pair<int, int>q[N];
vector<int>vec[N];

struct BIT{
	int v[N];

	void add(int x, int y){
		assert(x);
		for(; x<=m; x+=x&-x) v[x] += y;
	}

	int ask(int x){
		int res = 0;
		for(; x; x-=x&-x) res += v[x];
		return res;
	}
}T;

int head[N], to[N<<1], nxt[N<<1], tot;
void add(int x, int y){	
	to[++tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
}

int dep[N], size[N], heavy[N], fa[N];
void dfs(int x, int p){
	size[x] = 1, dep[x] = dep[p] + 1, fa[x] = p;	

	for(int i=head[x]; i; i=nxt[i]){
		int y = to[i];
		if(y == p) continue;
		dfs(y, x);
		size[x] += size[y];
		if(size[y] > size[heavy[x]]) heavy[x] = y;
	}
}

int dfn[N], top[N], num, edfn[N];
void dfsn(int x, int tp){
	dfn[x] = ++num, top[x] = tp, edfn[num] = x;

	if(!heavy[x]) return;
	dfsn(heavy[x], tp);

	for(int i=head[x]; i; i=nxt[i]){
		int y = to[i];
		if(y == fa[x] or y == heavy[x]) continue;

		dfsn(y, y);
	}
}

int glca(int x, int y){
	while(top[x] != top[y])
	{
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		x = fa[top[x]];
	}
	if(dep[x] < dep[y]) swap(x, y);
	return y;
}

int qlca(int l, int r){
	int k = log2(r - l + 1);
	return glca(st[l][k], st[r-(1<<k)+1][k]);
}

struct ODT{
	int l, r;
	mutable int v;

	ODT(const int &tl, const int &tr, const int &tv){l = tl, r = tr, v = tv;}
	bool operator < (const ODT &A)const{return l < A.l;}
};

set<ODT>odt;
typedef set<ODT>::iterator iter;

iter spilt(int x){
	iter it = --odt.upper_bound((ODT){x, 0, 0});
	if(it -> l == x) return it;
	assert(it != odt.end()); // if need go on,or will return
	int l = it -> l, r = it -> r, v = it -> v;
	odt.erase(it); odt.insert((ODT){l, x-1, v});
	return odt.insert((ODT){x, r, v}).first;
}

void assign(int l, int r, int v){
	iter itr = spilt(r + 1), itl = spilt(l);
	for(auto i=itl; i!=itr; i++) T.add(m-i->v+1, -(i->r-i->l+1));
	odt.erase(itl, itr); // erase the all area in [itl, itr).
	odt.insert((ODT){l, r, v});
	T.add(m-v+1, r-l+1);
}

void update(int x, int tim){
	while(x)
	{
		assign(dfn[top[x]], dfn[x], tim);
		x = fa[top[x]];
	}
}

int main(){
	// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

	cin>>n>>m>>Q; 
	odt.insert((ODT){1, n, 0});

	for(int i=1; i<=n-1; i++){
		int x, y; cin>>x>>y;
		add(x, y), add(y, x);
	}
	for(int i=1; i<=m; i++){
		cin>>a[i]; st[i][0] = a[i];
	}

	dfs(1, 0), dfsn(1, 1);

	for(int i=1; i<=17; i++){
		for(int j=1; j+(1<<i)-1<=m; j++){
			st[j][i] = glca(st[j][i-1], st[j+(1<<i-1)][i-1]);
		}
	}

	for(int i=1; i<=Q; i++){
		cin>>q[i].first>>q[i].second;
		ans[i] = dep[1] - dep[qlca(q[i].first, q[i].second)];
		vec[q[i].second].emplace_back(i);
		// cout<<dep[1]<<"\n";
	}

	for(int i=1; i<=m; i++){
		update(a[i], i);
		for(auto j:vec[i]) ans[j] += T.ask(m - q[j].first + 1);
	}

	for(int i=1; i<=Q; i++) cout<<ans[i]<<"\n";


	return 0;
}
posted @   Aqr_Rn  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示