那坍塌废墟 铺满尘垢 回忆中 谁仍昂着头 谁撕开 簇拥的伤口 搅动一汪 腐烂的血肉

test35

7-A 火车站 (train.cpp)

首先对二元组的入栈时间排序出一个出栈时间序列,你就是想要掰成尽可能少的单调下降子序列,如果你有直接并且极度自信你可以直接输出最长上升子序列的长度。考虑这个怎么连接起来,你顺序考虑出栈时间,每次就是找一个更大的替换掉,你去 lower_bound 是不劣的因为如果叉开你哪个放在前面都一样,直接上 set 维护即可。影响的只有这一步会计算成相等的贡献,然后这个过程等价于寻找最长上升子序列的过程,二分维护 dp 数组那种,还算比较精妙的(?

#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=200005;

int n, a[N], b[N], ret[N], Ans;
set<int> qwq;

signed main() {
	freopen("train.in","r",stdin);
	freopen("train.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n;
	up(i,1,n) cin >> a[i], ret[a[i]]=i;
	up(i,1,n) cin >> b[i];
	up(u,1,n) {
		int i=b[ret[u]];
		auto it=qwq.lower_bound(i);
		if(it!=qwq.end()) qwq.erase(it);
		qwq.insert(i);
		Ans=max(Ans,(int)qwq.size());
	}
	cout << Ans << '\n';
	return 0;
}

7-B 摄像头 (camera.cpp)

容易发现状态关心 \(f[u][0/1][0/1]\) 表示子树 \(u\) 根的状态是 \(0/1\) 对父亲有没有操作贡献,转移是大的分类讨论。

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

int n,a[100005];
vector<int> s[100005];
long long dp[2][2][100005];

void dfs(int x,int fa){
//	cout<<x<<" "<<fa<<endl;
	if(s[x].size()==1&&fa!=0){
		dp[0][a[x]][x]=0;
		dp[1][!a[x]][x]=1;
		dp[0][!a[x]][x]=n+1;
		dp[1][a[x]][x]=n+1;
		return;
	}
	long long qwq0=0,tj0=0,qwq1=0,tj1=0;
	bool lsy=1;
	// 00 
	for(int i=0; i<s[x].size(); ++i){
		int v=s[x][i];
		if(v==fa)continue;
		dfs(v,x);
		if(lsy) {
			lsy=0;
			qwq0=dp[0][0][v];
			qwq1=dp[1][0][v];
			tj0=dp[0][1][v];
			tj1=dp[1][1][v];
			continue;
		}
		long long zs0=qwq0,zs1=qwq1,zs00=tj0,zs01=tj1;
		qwq0=min(zs0+dp[0][0][v],zs1+dp[1][0][v]);
		qwq1=min(zs1+dp[0][0][v],zs0+dp[1][0][v]);
		tj0=min(zs00+dp[0][1][v],zs01+dp[1][1][v]);
		tj1=min(zs01+dp[0][1][v],zs00+dp[1][1][v]);
	}
	dp[0][a[x]][x]=min(n+1ll,qwq0);
	dp[0][!a[x]][x]=min(n+1ll,qwq1);
	dp[1][!a[x]][x]=min(n+1ll,tj0)+1;
	dp[1][a[x]][x]=min(n+1ll,tj1)+1;
//	cout<<x<<":  "<<dp[0][a[x]][x]<<" "<<dp[0][!a[x]][x]<<" "<<dp[1][a[x]][x]<<" "<<dp[1][!a[x]][x]<<endl;
	return;
}

int main(){
	//cout<<1<<endl;
	freopen("camera.in","r",stdin);
	freopen("camera.out","w",stdout);
	scanf("%d",&n);
	for(int i(1);i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		s[x].push_back(y);
		s[y].push_back(x);
	}
	for(int i(1);i<=n;i++)scanf("%d",&a[i]);
	dfs(1,0);
	long long ans=min(dp[0][0][1],dp[1][0][1]);
	if(ans>n)printf("impossible\n");
	else printf("%lld",ans);
	return 0;
}

7-C 道路拆除 (road.cpp)

四舍五入题目就是要求对于非树边满足其权值大于所有树边,你考虑从小到大编号贪心,你先贪心邻域可以拿到当前编号的最优,没有后效性、先做别的肯定更劣。问题只剩下怎么快速找树链上没有编号的边,并对它们排序,只需要保证没有重复遍历即可,向上维护一个 dsu 就好了。

#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 pii pair<int,int>
#define mp make_pair
#define pb push_back

using namespace std;

const int N=300005, M=21;

int n, m, T, id[N], x[N], y[N], diff[N], val[N];
int sav[N], dep[N], fa[N][M], dsu[N], tot;
vector<pii> to[N];

void dfs(int x,int fad) {
	dep[x]=dep[fad]+1, fa[x][0]=fad;
	up(i,1,T) fa[x][i]=fa[fa[x][i-1]][i-1];
	for(pii u:to[x]) {
		int y=u.first, i=u.second;
		if(y==fad) continue;
		dfs(y,x), sav[y]=i;
	}
}

int lca(int x,int y) {
	if(dep[y]>dep[x]) swap(x,y);
	dn(i,T,0) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
	if(x==y) return x;
	dn(i,T,0) if(fa[x][i]!=fa[y][i]) x=fa[x][i], y=fa[y][i];
	return fa[x][0]; 
}

inline int get(int x) { return x==dsu[x]?x:dsu[x]=get(dsu[x]); }

signed main() {
//	freopen("1.txt","r",stdin);
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m, T=__lg(n);
	up(i,1,m) cin >> x[i] >> y[i];
	up(i,2,n) {
		int u;
		cin >> u, diff[u]=1;
		to[x[u]].pb(mp(y[u],u));
		to[y[u]].pb(mp(x[u],u)); 
	}
	dfs(1,0);
	up(i,1,n) dsu[i]=i;
	up(i,1,m) {
		if(!diff[i]) {
			int l=x[i], r=y[i], p=lca(l,r);
			vector<int> now;
			while(dep[l=get(l)]>dep[p]) now.pb(sav[l]), dsu[l]=fa[l][0];
			while(dep[r=get(r)]>dep[p]) now.pb(sav[r]), dsu[r]=fa[r][0];
			sort(now.begin(),now.end());
			for(int u:now) val[u]=++tot;
		}
		if(!val[i]) {
			val[i]=++tot;
			int p=dep[x[i]]>dep[y[i]]?x[i]:y[i];
			dsu[p]=fa[p][0];
		} 
	}
	up(i,1,m) cout << val[i] << ' ';
	return 0;
}

7-D 游戏 (game.cpp)

先来观察一下,假设最后留下了 \(p_1(=1),\dots,p_m\),那么 \([p_i,p_{i+1})\) 的删除是独立的,所以可以划开 \(f_u\) 表示考虑了 \(str[u\to n]\) 的答案,然后我们来考虑什么样的 \(j\) 可以转移到 \(i\) 也就是什么样的 \([i,j)\) 可以被删到只剩下 \(str_i\)

固定 \(i,j\),显然扫描 \(j-1\to i\) 能删就删不劣,进一步的,我们发现合法的 \(j\) 是靠着 \(i\) 的连续一段,因为有卡着的你往前缀再怎么加也没有,所以我们现在只要快速找到区间里面的最小 \(f\) 即可,可以倍增考虑最大的相同前缀、哈希判断是否相等来写小于号,然后前缀 \(\min\) 可以写上一个单调栈+二分。

#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 pii pair<int,int>
#define mp make_pair
#define pb push_back

using namespace std;

const int N=500005, M=31, B=233, P1=1e9+7, P2=998244353;

int n, k, T, chk[M][M], a[N], to[N][M], len[N], stk[N], top;
int pwf[N], pwg[N], f[N][M], g[N][M];
char str[N];
stack<int> qwq;

bool leq(int i,int j) {
	dn(u,T,0) if(to[i][u]&&f[i][u]==f[j][u]&&g[i][u]==g[j][u]) i=to[i][u], j=to[j][u];
	return (f[i][0]==f[j][0])?(len[i]<len[j]):(f[i][0]<f[j][0]);
}

void print(int x) {
	if(x>n) return;
	cout << str[x], print(to[x][0]);
}

signed main() {
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> k >> n, T=__lg(n), pwf[0]=pwg[0]=1;
	up(i,1,n) pwf[i]=B*pwf[i-1]%P1, pwg[i]=B*pwg[i-1]%P2;
	up(i,0,k-1) {
		cin >> str;
		up(j,0,k-1) chk[i][j]=(str[j]=='1');
	}
	cin >> (str+1);
	up(i,1,n) a[i]=(str[i]-'a');
	stk[top=1]=n+1;
	dn(i,n,1) {
		while(qwq.size()&&chk[a[i]][a[qwq.top()]]) qwq.pop();
		int lim=qwq.size()?qwq.top():n+1, l=1, r=top, j;
		while(l<=r) {
			int mid=(l+r)>>1;
			if(stk[mid]<=lim) j=stk[mid], r=mid-1;
			else l=mid+1;
		}
		to[i][0]=j, f[i][0]=g[i][0]=str[i], len[i]=len[j]+1;
		up(u,1,T) if(i+(1<<u)-1<=n) {
			to[i][u]=to[to[i][u-1]][u-1];
			f[i][u]=(f[i][u-1]+f[to[i][u-1]][u-1]*pwf[1<<(u-1)]%P1)%P1;
			g[i][u]=(g[i][u-1]+g[to[i][u-1]][u-1]*pwg[1<<(u-1)]%P2)%P2;
		}
		while(top&&leq(i,stk[top])) --top;
		stk[++top]=i, qwq.push(i);
	}
	print(1);
	return 0;
}
posted @ 2025-11-09 20:00  Hypoxia571  阅读(11)  评论(0)    收藏  举报