被称作遗憾之物 爬满了脊骨 又把控了痛楚 被称作无用之物 修筑了唯一的通路

test30

前两题都 0pts,nbm(?

2-A 飞船制造 (spaceship.cpp)

怎么有傻子没开 c++11 写了 rank 然后 re 惹 /fad

考虑依次枚举 \(s=i+j+k\),计算出 \(s\) 一定的方案数就能确定唯一的 \(s\),方案数计算好像只能考虑背包。然后依次枚举 \(i\),计算出 \(s,i\) 一定时 \((j,k)\) 的方案数就能确定唯一的 \(i\),方案数可以考虑 \(j\) 的上下界。然后依次枚举 \(j\),不断增加排名直到符合题目要求即可。

#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)

using namespace std;

const int N=3000005;

int n, rk, f[3][N];

signed main() {
//	freopen("1.txt","r",stdin);
	freopen("spaceship.in","r",stdin);
	freopen("spaceship.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> rk;
	up(i,1,3*n) f[0][i]=f[0][i-1]+(i<=n);
	up(i,1,3*n) f[1][i]=f[0][i-1]-(i-n-1>=0?f[0][i-n-1]:0);
	up(i,1,3*n) f[1][i]+=f[1][i-1];
	up(i,1,3*n) f[2][i]=f[1][i-1]-(i-n-1>=0?f[1][i-n-1]:0);
	int counter=0;
	up(s,3,3*n) {
		int val=f[2][s];
		if(counter+val>=rk) { 
			up(i,1,n) {
				int l=max(1ll,s-i-n);
				int r=min(n,s-i-1);
				if(l<=r) {
					if(counter+r-l+1>=rk) {
						up(j,1,n) {
							if(1<=s-i-j&&s-i-j<=n) {
								if(++counter==rk) {
									cout << i << ' ' << j << ' ' << s-i-j << '\n';
									return 0;
								}
							}
						}
					}
					counter+=r-l+1;
				}
			}
		} 
		counter+=val;
	}
	return 0;
} 

2-B 机器人 (robot.cpp)

怎么有人下发错误的 statement,谋害本可使爆零,以及这个原题“阻碍关系具有传递性”想何出什么样的意味(?

发现 \(n\leq 1000\),努力去想简单暴力的办法,考虑所有的可能的阻碍对 \((i,j,t)\) 表示 \(i\) 可能在 \(t\) 时刻阻碍 \(j\),然后去顺次考虑每一个点对生不生效。因为题目给了一些互不相等所以不用去纠边角情况。发现只要动态维护 \(u\) 有没有被阻碍、以及被阻碍到哪个地方就能判断生效情况以及更新阻碍情况,不尽之处皆是一些简单分讨(?

#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pb push_back

using namespace std;

const int N=1005, M=1000005;

int n, m, x[N], y[N], tag[N], in[N], Ans[N];
vector<int> to[N];
char opt[N];
struct node {
	int i, j, t;
	bool operator<(const node &rhs) const { return t<rhs.t; }
} p[M];

void dfs(int x) {
	for(int y:to[x]) dfs(y), Ans[x]+=Ans[y]+1;
}

signed main() {
	freopen("robot.in","r",stdin);
	freopen("robot.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n;
	up(i,1,n) cin >> opt[i] >> x[i] >> y[i];
	up(i,1,n) {
		if(opt[i]!='N') continue;
		up(j,1,n) if(opt[j]=='E') {
			if(x[j]>x[i]||y[j]<y[i]) continue;
			int ti=y[j]-y[i], tj=x[i]-x[j];
			if(ti==tj) continue;
			if(ti<tj) p[++m]=(node){i,j,tj};
			if(tj<ti) p[++m]=(node){j,i,ti};
		}
	}
	sort(p+1,p+1+m);
	up(u,1,m) {
		int i=p[u].i, j=p[u].j;
//		cout << "breat " << i << ' ' << j << '\n';
		if(!tag[j]&&(!tag[i]||opt[i]=='N'&&y[j]<=y[i]+tag[i]
			||opt[i]=='E'&&x[j]<=x[i]+tag[i])) {	
			tag[j]=p[u].t, to[i].pb(j), ++in[j];
		}
	}
	up(i,1,n) if(!in[i]) dfs(i);
	up(i,1,n) cout << Ans[i] << '\n';
	return 0;
}

2-C 总统大选 (election.cpp)

大样例全过挂分的来了。上次见到你,你叫作《机械师》。

贪心,首先先拿 \(a\) 再拿 \(b\) 显然是劣的,考虑操作拿 \(b_1,\dots,b_m,a_{m+1},\dots,a_{m_0}\),答案就是 \(\sum_{i=1}^m \frac{b_i}{i}+\sum_{i=m+1}^{m}\frac{a_i}{m+1}\)。最终的操作序列一定是按照 \(b\uparrow\) 的顺序获得协作者,然后在剩下的州里面挑 \(a\) 最小的或者选票直到足够。按照这个做是按照 \(b\uparrow\) 排序,枚举协作者总人数 \(m\),然后背包 \(f[i][j][k]\) 表示考虑前 \(i\) 个人选了 \(j\) 个协作者 \(k\) 个演讲的最小时间,答案是 \(\min_{m=0}^{m_0}\{f[n][m][m_0-k]\}\),还需要优化掉一个 \(O(n)\)

直觉上我们希望拿最小的若干个 \(b\) 再拼凑上若干个后缀上最小的 \(a\),但是这么做是错误的,感性理解就是有些点虽然 \(b\) 不大但 \(a\) 尤其小,其拿 \(a\) 的性价比太高了,具体而言假定 \((a_1,b_1),(a_2,b_2)\) 要协作一个选票一个,设 \(b_1>b_2\),协作 1 比协作 2 优的条件形如 \(b_1+\frac{a_2}{2}<b_2+\frac{a_1}{2}\),也就是 \(2(b_2-b_1)<(a_1-a_1)\) 是有可能被满足的。

但是不影响扣掉这些被 \(a\) 的东西之后协作者堆成一个前缀,考虑处理前缀非 \(a\)\(b\) 的答案之后往后合并即可,相当于额外加了一个枚举前缀位置。

只做 \(\forall i\in[1,m_0],j+k=i\) 就足够贡献了,还是枚举 \(m\),那么新的状态计算 \(f[i][j]\) 表示考虑了前 \(i\) 个州选了 \(j\) 个协作者的最小时间,计算后缀选 \(u\)\(a\) 的最小时间和即可合并出答案。

#include<bits/stdc++.h>
#define int long long
#define db double
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair 
#define a first
#define b second

using namespace std;

const int N=505, inf=1e13;

int n, m, arr[N], sum[N][N];
pii p[N];
db Ans=inf, f[N][N];

inline void chk(db &a,db b) { a=min(a,b); }

signed main() {
	freopen("election.in","r",stdin);
	freopen("election.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m;
	up(i,1,n) {
		cin >> p[i].a >> p[i].b;
		if(p[i].b==-1) p[i].b=inf;
	}
	sort(p+1,p+1+n,[](pii i,pii j){return i.b<j.b;});
	dn(i,n,1) {
		int tot=0;
		up(j,i,n) arr[++tot]=p[j].a;
		sort(arr+1,arr+1+tot);
		up(j,1,n-i+1) sum[i][j]=sum[i][j-1]+arr[j];
	}
	up(k,0,m) {
		up(i,0,m) up(j,0,k) f[i][j]=inf;
		f[0][0]=0;
		up(i,1,m) up(j,0,min(i,k)) {
			chk(f[i][j],f[i-1][j]+(db)p[i].a/(k+1));
			chk(f[i][j+1],f[i-1][j]+(db)p[i].b/(j+1));
		}
		up(i,0,m) chk(Ans,f[i][k]+(db)sum[i+1][m-i]/(k+1));
	}
	cout << fixed << setprecision(15) << Ans << '\n';
	return 0;
}

2-D 风力发电机 (wind.cpp)

我是怎么做到不同 mst 最后才考虑重构树的,题目有点像模板套模板套模板嗯对。

因为求解答案是加入权值为 \(0\) 的边重新求 mst,所以实际上要使用的边肯定是原 mst 的子集,复杂问题进一步的我们建立出重构树,显然这样子可以体现 mst 更好的性质(权值递增、集合合并、顺序明确)。

现在考虑选定一些特殊点怎么在 mst 上考虑贡献,假设只有 \(p,q\) 就是删除 \(lca(p,q)\),因为能删除的是 \(p/q\to lca(p,q)\) 的边,而越上面的越不劣。进一步考虑很多个点 \(p_1,\dots,p_m\) 的情况,有点像虚树因为会刚好新建立 \(m-1\) 个虚点诶,更具体的可以考虑手摸 \(m\) 比较小的情况,发现可以归纳法看到贡献是对的。

进一步考虑一个点什么时候会成为虚点,显然是左右子树皆不为空的情况,就是存在 \((p,q)\) 满足 \(l\leq p<q\leq r\) 然后 \(p/q\) 分别在左右子树里面吧,哦发现 dsu on tree 点对个数只有 \(O(n\log n)\),可以处理出来。现在考虑怎么求解答案,就是对于 \(l,r\) 查询 \((p,q,w)\) 满足 \(l\leq p<q\leq r\) 的不同 \(w\) 的权值和,扫描线处理掉一维,然后要对 \(q\leq r\) 的每个 \(w\) 考虑 \(\max p\),打在树状数组上即可快速查询。

#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pb push_back

using namespace std;

const int N=200005, M=5000005;

int n, m, T, len, dsu[N], tot, val[N], ls[N], rs[N], Ans[N], lst[N], tr[N], pre;
struct edge { int u, v, w; } e[N], p[M], q[N];
vector<int> to[N];
set<int> point[N];

int get(int x) { return x==dsu[x]?x:dsu[x]=get(dsu[x]); }
void add(int x,int v) { for( ; x<=n; x+=x&-x) tr[x]+=v; }
int ask(int x) { int ret=0; for( ; x; x-=x&-x) ret+=tr[x]; return ret; }

int dfs(int x) {
	if(x<=n) {
		point[x].insert(x);
		return x;
	}
	int l=dfs(ls[x]), r=dfs(rs[x]); 
	if(point[l].size()>point[r].size()) swap(l,r);
	for(int val:point[l]) {
		auto it=point[r].lower_bound(val);
		if(it!=point[r].end()) p[++len]=(edge){val,*it,x};
		if(it!=point[r].begin()) p[++len]=(edge){*--it,val,x};
	}
	for(int val:point[l]) point[r].insert(val);
	point[l].clear();
	return r;
}

signed main() {
	freopen("wind.in","r",stdin);
	freopen("wind.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m >> T, tot=n;
	up(i,1,m) cin >> e[i].u >> e[i].v >> e[i].w, ++e[i].u, ++e[i].v;
	sort(e+1,e+1+m,[](edge i,edge j){return i.w<j.w;});
	up(i,1,n) dsu[i]=i;
	up(i,1,m) {
		int x=get(e[i].u), y=get(e[i].v);
		if(x!=y) {
			pre+=e[i].w, val[++tot]=e[i].w, dsu[tot]=tot;
			ls[tot]=x, rs[tot]=y, dsu[x]=dsu[y]=tot;
//			cout << " " << tot << "(" << e[i].w << ")"<< ' ' << ls[tot] << '\n';
//			cout << " " << tot << "(" << e[i].w << ")"<< ' ' << rs[tot] << '\n';
		}
	}
	dfs(tot);
	sort(p+1,p+1+len,[](edge i,edge j){return i.v<j.v;});
//	up(i,1,len) cout << "check " << p[i].u << ' ' << p[i].v << ' ' << p[i].w << '\n';
	up(i,1,T) q[i].w=i, cin >> q[i].u >> q[i].v, ++q[i].u, ++q[i].v;
	sort(q+1,q+1+T,[](edge i,edge j){return i.v<j.v;});
	int j=1;
	up(i,1,T) {
		while(j<=len&&p[j].v<=q[i].v) {
//			cout << "insert " << p[j].u << ' ' << p[j].v << ' ' << val[p[j].w] << '\n';
			int id=p[j].w;
			if(lst[id]) {
				if(lst[id]<p[j].u) {
					add(lst[id],-val[id]);
					lst[id]=p[j].u;
					add(p[j].u,val[id]);
				}
			}
			else add(p[j].u,val[id]), lst[id]=p[j].u;
			++j;
		}
		Ans[q[i].w]=ask(n)-ask(q[i].u-1);
	}
	up(i,1,T) cout << pre-Ans[i] << '\n';
	return 0;
}
posted @ 2025-10-28 20:03  Hypoxia571  阅读(8)  评论(0)    收藏  举报