恩偶挨批模拟试题-17

水博客太快乐了

RT

考场

挂了。

分数

也挂了。

题解

A. 世界线

观察这题的本质,实际上就是每个点向所有它可以到达的点连一条边,但是已经有 \(m\) 条边了,所以答案减掉即可,不难想到联通数一题,考虑用 \(bitset\) 记下每个点可到达的点,相当于记忆化搜索。

但是这题卡空间, \(bitset\) 只能开一半,所以先统计一下能到达的小于等于 \(\frac{n}{2}\) 的点的个数,再统计一下能到达的大于 \(\frac{n}{2}\) 的个数,加一起即可。

code
#include<bits/stdc++.h>
using namespace std;
const int N=6e4+10;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int n, m;
int ans, ul;
bitset<(N>>1)> d[N];
vector<int> l[N];
bool vis[N];
void dfs(int u, bool op){
	if(vis[u]) return;
	vis[u]=1;
	for(int v : l[u]){
		if(!vis[v]) dfs(v, op);	d[u]|=d[v];
		if(!op&&v<=ul) d[u][v]=1;
		if(op&&v>ul) d[u][v-ul]=1;
	}
	ans+=d[u].count();
}
int main(void){
	n=read(), m=read(); int x, y;
	for(int i=1; i<=m; ++i){
		x=read(), y=read();
		l[x].push_back(y);
	}
	ul=n>>1; ans-=m;
	for(int i=1; i<=n; ++i) if(!vis[i]) dfs(i, 0);
	for(int i=1; i<=n; ++i) d[i]&=d[0];
	memset(vis, 0, sizeof vis);
	for(int i=1; i<=n; ++i) if(!vis[i]) dfs(i, 1);
	printf("%d\n", ans);
	return 0;
}

B. 时间机器

考虑先按 \(l\) 排序,然后贪心,对于 \(l\) 所有小于当前节点的电池,优先使用 \(r\) 最接近这个节点的电池。用某种数据结构维护,可以用 \(set\) 或权值线段树。
为什么是正确的?显然是正确的。。。

code
#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int N=5e4+10;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int T, n, m;
struct node{
	int l, r, num;
	friend bool operator < (const node &x, const node &y) { return x.l < y.l; }
}a[N], b[N];
struct ul{
	int r;
	mutable int num;
	ul(int r=0, int num=0):r(r),num(num){}
	friend bool operator < (const ul &x, const ul&y) { return x.r!=y.r ? x.r<y.r : x.num<y.num; }
};
multiset<ul> s;
void solve(){
	s.clear();
	n=read(), m=read();
	for(int i=1; i<=n; ++i) a[i]=(node){read(), read(), read() };
	for(int i=1; i<=m; ++i) b[i]=(node){read(), read(), read() };
	sort(a+1, a+1+n); sort(b+1, b+1+m);
	for(int i=1, j=1; i<=n; ++i){
		for(; j<=m&&b[j].l<=a[i].l; ++j) s.insert(ul(b[j].r, b[j].num));
		while(a[i].num){
			auto it=s.lower_bound(ul(a[i].r, 0));
			if(it==s.end()) { puts("No"); return; }
			if(it->num>a[i].num) { it->num-=a[i].num; break; }
			a[i].num-=it->num; s.erase(it);
		}
	}
	puts("Yes");
}
signed main(void){
	T=read();
	while(T--) solve();
	return 0;
}

C. weight

裸的 \(MST\) 加树剖线段树。。

code
#include<bits/stdc++.h>
using namespace std;
#define t first
#define w second
const int N=7e4+10, M=1e5+10, INF=0x7fffffff;
inline int read(){
	int f=1, x=0; char ch=getchar();
	while(!isdigit(ch)) { if(ch=='-') f=-1; ch=getchar(); }
	while(isdigit(ch)) { x=x*10+ch-48; ch=getchar(); }
	return f*x;
}
int n, m, a;
int fa[N], top[N], dep[N], siz[N], val[N], son[N], num[N], num1[N], ind;
vector<pair<int, int> > l1[N];
struct TRE{
	int l, r;
	int minn, maxn;
}t[N<<2];
struct EDGE{
	int f, t, w, id, ans;
	bool ul;
	friend bool operator < (const EDGE &x, const EDGE &y) { return x.w < y.w; }
}l[M];
bool cmp(const EDGE &x, const EDGE &y) { return x.id < y.id; }
void built(int l, int r, int p){
	t[p].l=l, t[p].r=r, t[p].minn=INF;
	if(l==r) { t[p].maxn=val[num1[l]]; return; }
	int mid=(l+r)>>1;
	built(l, mid, p<<1); built(mid+1, r, p<<1|1);
	t[p].maxn=max(t[p<<1].maxn, t[p<<1|1].maxn);
}
void change(int l, int r, int x, int p){
	if(l>r) return;
	if(l<=t[p].l&&r>=t[p].r) { t[p].minn=min(t[p].minn, x); return; }
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid) change(l, r, x, p<<1);
	if(r>mid) change(l, r, x, p<<1|1);
}
int query(int l, int r, int p){
	if(l>r) return 0;
	if(l<=t[p].l&&r>=t[p].r) return t[p].maxn;
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid&&r>mid) return max(query(l, r, p<<1), query(l, r, p<<1|1));
	if(l<=mid) return query(l, r, p<<1);
	return query(l, r, p<<1|1);
}
void dfs3(int minn, int p){
	minn=min(minn, t[p].minn);
	if(t[p].l==t[p].r) { val[num1[t[p].l]]=minn; return; }
	dfs3(minn, p<<1); dfs3(minn, p<<1|1);
}
void dfs1(int u, int f){
	fa[u]=f; dep[u]=dep[f]+1; siz[u]=1;
	for(pair<int, int > v : l1[u]){
		if(v.t==f) continue; val[v.t]=v.w;
		dfs1(v.t, u); siz[u]+=siz[v.t];
		if(siz[v.t]>siz[son[u]]) son[u]=v.t;
	}
}
void dfs2(int u, int tp){
	top[u]=tp; num[u]=++ind, num1[ind]=u;
	if(son[u]) dfs2(son[u], tp);
	for(pair<int, int> v : l1[u]) if(v.t!=son[u]&&v.t!=fa[u]) dfs2(v.t, v.t);
}
int ask(int u, int v, int w){
	int ans=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) u^=v, v^=u, u^=v;
		ans=max(ans, query(num[top[u]], num[u], 1));
		change(num[top[u]], num[u], w, 1); u=fa[top[u]];
	}
	if(dep[u]<dep[v]) u^=v, v^=u, u^=v;
	change(num[v]+1, num[u], w, 1); ans=max(ans, query(num[v]+1, num[u], 1));
	return ans;
}
int f[N];
bool vis[N];
int find(int x) { return x==f[x] ? x : f[x]=find(f[x]); }
inline void pre(){
	sort(l+1, l+1+m); int cnt=0, u, v;
	for(int i=1; i<=n; ++i) f[i]=i;
	for(int i=1; i<=m; ++i){
		u=find(l[i].f), v=find(l[i].t);
		if(v!=u) f[u]=v;
	}
	int ul=0;
	for(int i=1; i<=n; ++i) if(find(1)==find(i)) vis[i]=1, ul++;
	for(int i=1; i<=n; ++i) f[i]=i;
	for(int i=1; i<=m&&cnt<ul-1; ++i){
		if(!vis[l[i].f]||!vis[l[i].t]) continue;
		u=find(l[i].f); v=find(l[i].t);
		if(u==v) continue; ++cnt;
		f[u]=v; u=l[i].f, v=l[i].t; l[i].ul=1;
		l1[u].push_back(make_pair(v, l[i].w));
		l1[v].push_back(make_pair(u, l[i].w));
	}
	dfs1(1, 0); dfs2(1, 1); built(1, ul, 1);
}
inline void solve(){
	for(int i=1; i<=m; ++i){
		if(l[i].ul) continue;
		l[i].ans=ask(l[i].f, l[i].t, l[i].w);
	}
	dfs3(INF, 1);
	for(int i=1; i<=m; ++i){
		if(!l[i].ul||!vis[l[i].f]||!vis[l[i].t]) continue;
		if(fa[l[i].f]==l[i].t) l[i].ans=val[l[i].f];
		else l[i].ans=val[l[i].t];
	}
	sort(l+1, l+1+m, cmp);
	for(int i=1; i<=m; ++i){
		if(l[i].ans==INF) printf("-1 "); 
		else if(!l[i].ans) printf("0 ");
		else printf("%d ", --l[i].ans);
	}
	printf("\n");
}
int main(void){
	n=read(); m=read(); a=read();
	int x, y, z;
	for(int i=1; i<=m; ++i){
		x=read(), y=read(), z=read();
		l[i]=(EDGE){x, y, z, i, 0, 0};
	}
	pre(); solve();
	return 0;
}

当然也可以用 \(LCT\) 不过我懒得写了,存下巨佬的代码

posted @ 2021-07-17 12:17  Cyber_Tree  阅读(35)  评论(0编辑  收藏  举报