2025 GJ 的 CSP-S/NOIP 模拟赛49 总结

得分:\(100+100+76+77\)

赛时以为 T3 不可做,把暴力打满了,之后会了正解但没调出

T4 成功 \(O(n \times 树高)\) 骗分

U636246 C49-T1

有点诈骗

直接做过于困难,考虑建图,相同的字符串代表相同的节点,然后 \(s_i \rightarrow t_i\) 连边,那么是内向基环森林,链上的点可以按照拓扑逆序来重命名,\(s_i = t_i\) 的不用重命名,有几个环就多重命名几次

#include <bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i = a; i <= b; i++)
#define F_(i,a,b) for(int i = a; i >= b; i--)
#define pii pair<int,int>
#define fr first
#define sc second
#define mem0(a) memset(a,0,sizeof(a))
#define pb push_back
#define lb(x) (x&(-x))
#define lson u<<1
#define rson u<<1|1
typedef long long ll;
typedef unsigned long long ull;
const int N = 2e5+10,M = 1e4+2;
const double eps = 1e-5;
const ll Mod = 1e9+7;
// huangyuze
int n,tot,rd[N],inh[N],vis[N];
map<string,int> mp;
vector<int> go[N];
void topo(){
	queue<int> q;
	F(i,1,n) if (!rd[i]) q.push(i);
	F(i,1,n) inh[i] = 1;
	while (q.size()){
		int u = q.front(); q.pop();
		inh[u] = 0;
		for (auto v:go[u]){
			rd[v]--;
			if (!rd[v]) q.push(v);
		}
	}
}
void dfs(int u){
	vis[u] = 1;
	for (auto v:go[u]) if (!vis[v]) dfs(v);
}
int main(){
	freopen("files.in","r",stdin);
	freopen("files.out","w",stdout);
	cin >> n;
	int ans = n;
	F(i,1,n){
		string s,t;
		cin >> s >> t;
		if (!mp[s]) mp[s] = ++tot;
		if (!mp[t]) mp[t] = ++tot;
		if (s == t){ans--; continue;}
		go[mp[s]].pb(mp[t]);
		++rd[mp[t]];
	}
	topo();
	F(i,1,n) if (inh[i] && !vis[i]) ++ans, dfs(i);
	cout << ans;
	return 0;
}

U636248 C49-T2

观察到 \(n\) 较小,考虑处理出打死 \(i\) 个怪物的最小代价,查询时二分即可

难点在这个处理,贪心难以做到。通过拆贡献,我们发现当选出的怪物集合固定时,打怪物的顺序肯定是按照 \(a_i+b_i\) 从小到大,于是我们先把 \(a_i+b_i\) 从小到大排序,再跑一次 dp,\(f_{i,j}\) 表示前 \(i\) 个怪物中打死 \(j\) 个的最小代价,这样就是对的

#pragma GCC optimize(2,3,"Ofast","inline")
#include <bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i = a; i <= b; i++)
#define F_(i,a,b) for(int i = a; i >= b; i--)
#define pii pair<int,int>
#define fr first
#define sc second
#define mem0(a) memset(a,0,sizeof(a))
#define pb push_back
#define lb(x) (x&(-x))
#define lson u<<1
#define rson u<<1|1
typedef long long ll;
typedef unsigned long long ull;
const int N = 3e3+10,M = 3e5+10;
const double eps = 1e-5;
const ll Mod = 1e9+7;
// huangyuze
int n,m;
struct D{ll a,b;} d[N];
ll ad,dp[N][N],res[N];
bool cmp(D x,D y){
	return x.a+x.b > y.a+y.b;
}
int main(){
	freopen("hunter.in","r",stdin);
	freopen("hunter.out","w",stdout);
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> n >> m >> ad;
	F(i,1,n) cin >> d[i].a;
	F(i,1,n) cin >> d[i].b;
	sort(d+1,d+n+1,cmp);
	F(i,0,n) F(j,0,n) dp[i][j] = 1e18;
	dp[0][0] = 0;
	F(i,1,n) F(j,0,i){
		if (!j) dp[i][j] = 0;
		else dp[i][j] = min(dp[i-1][j],dp[i-1][j-1] + (d[i].a+(j-1)*ad)*(d[i].b+(j-1)*ad));
	}
	F(i,1,n) res[i] = dp[n][i];
	F(i,1,m){
		ll x; cin >> x;
		int pos = lower_bound(res+1,res+n+1,x)-res-1;
		cout << pos << " ";
	}
	return 0;
}
/*
a_1 b_1 + 0 d b_1 + 0 d a_1 + 0 d d
a_2 b_2 + 1 d b_2 + 1 d a_2 + 1 d d
a_3 b_3 + 2 d b_3 + 2 d a_3 + 4 d d

100 1  
1 101 
*/

U636250 C49-T3

从部分分到正解

注意到帽子交换后会重新编号,这是个很好的性质

考虑 \(O(nm+q)\) 怎么做,我们考虑预处理出 \(pos_{i,j}\) 表示球在第 \(i\) 个帽子经过前 \(j\) 轮到那个帽子,那么查询 \([l,r]\) 的交换操作时,我们的位置 \(x\) 一定可以通过从 \(1\) 开始某个位置的交换操作到达,设 \(whe_{i,j}\) 表示在前 \(j\) 轮操作后,球在那个位置能使球到 \(i\)。那么答案为 \(pos_{whe_{l-1,x},r}\)

模拟交换过程,一开始设 \(x_i = i\),然后每轮交换 \({x_{a_i},x_{b_i}}\)。观察到 \(j\) 轮后的 \(x_i\) 就是 \(whe_{i,j}\),而且 \(pos_{i,j}\)\(whe_{i,j}\) 对应,于是用可持久化线段树维护

#include <bits/stdc++.h>
using namespace std;
#define F(i,a,b) for(int i = a; i <= b; i++)
#define F_(i,a,b) for(int i = a; i >= b; i--)
#define pii pair<int,int>
#define fr first
#define sc second
#define mem0(a) memset(a,0,sizeof(a))
#define pb push_back
#define lb(x) (x&(-x))
#define lson u<<1
#define rson u<<1|1
typedef long long ll;
typedef unsigned long long ull;
const int N = 2e5+10,M = 1e4+2;
const double eps = 1e-5;
const ll Mod = 1e9+7;
// huangyuze
int n,m,q;
struct CZ{int a,b;} cz[N];
struct SGT{
	int rt[N],cnt;
	int val[N<<6],ls[N<<6],rs[N<<6];
	void build(int &u,int l,int r){
		u = ++cnt;
		if (l == r){
			val[u] = l;
			return ;
		}
		int mid = (l+r)>>1;
		build(ls[u],l,mid);
		build(rs[u],mid+1,r);
	}
	void update(int &u,int pre,int l,int r,int p,int d){
		u = ++cnt;
		if (l == r){
			val[u] = d;
		} else {
			ls[u] = ls[pre], rs[u] = rs[pre];
			int mid = (l+r)>>1;
			if (p <= mid) update(ls[u],ls[pre],l,mid,p,d);
			else update(rs[u],rs[pre],mid+1,r,p,d);
		}
	}
	int query(int u,int l,int r,int p){
		if (l == r){
			return val[u];
		}
		int mid = (l+r)>>1;
		if (p <= mid) return query(ls[u],l,mid,p);
		else return query(rs[u],mid+1,r,p);
	}
} pos,whe;
int main(){
	freopen("magic.in","r",stdin);
	freopen("magic.out","w",stdout);
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> n >> m >> q;
	F(i,1,m) cin >> cz[i].a >> cz[i].b;
	pos.build(pos.rt[0],1,n); whe.build(whe.rt[0],1,n);
	F(i,1,m){
		int px = pos.query(pos.rt[i-1],1,n,cz[i].a), py = pos.query(pos.rt[i-1],1,n,cz[i].b);
		int wx = whe.query(whe.rt[i-1],1,n,px), wy = whe.query(whe.rt[i-1],1,n,py);
		pos.update(pos.rt[i],pos.rt[i-1],1,n,cz[i].a,py); pos.update(pos.rt[i],pos.rt[i],1,n,cz[i].b,px);
		whe.update(whe.rt[i],whe.rt[i-1],1,n,px,wy); whe.update(whe.rt[i],whe.rt[i],1,n,py,wx);
	}
	F(i,1,q){
		int x,l,r;
		cin >> x >> l >> r;
		cout << whe.query(whe.rt[r],1,n,pos.query(pos.rt[l-1],1,n,x)) << "\n";
	}
	return 0;
}
/*
5 5 2
2 4
1 4
1 5
3 4
1 2
2 1 3
1 2 4


1 2 3 4 5
1 2 3 4 5

1 4 3 2 5
1 4 3 2 5

2 4 3 1 5
4 1 3 2 5

5 4 3 1 2
4 5 3 2 1
*/
posted @ 2025-11-27 17:01  huangyuze  阅读(2)  评论(0)    收藏  举报