感动渐渐化为平静的水面 虚假美好回忆变成不可逆爱恋

test47

跑路

题目看起来很复杂,我们不妨画出一条路径,然后发现是一段取反一段不取反,那么直接 \(f[i][j][0/1]\) 表示走到 \((i,j)\) 状态是不取反/取反最小步数,转移可以先直接转出去,\(f[i][j][0/1]\to f[i+1][j][0/1]/f[i][j+1][0/1]\),然后在点上考虑钦定反不反,\(f[i][j][0]+1\to f[i][j][1],f[i][j][0]=\infty/f[i][j][1]\to f[i][j][0],f[i][j][1]=\infty\)

#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=1005, inf=1e13;

int n, m, a[N][N], f[N][N][2];

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

signed main() {
	freopen("run.in","r",stdin);
	freopen("run.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m;
	up(i,1,n) up(j,1,m) cin >> a[i][j];
	memset(f,0x3f,sizeof(f));
	if(a[1][1]==0) f[1][1][0]=0; else f[1][1][1]=1;
	up(i,1,n) up(j,1,m) {
		if(a[i][j]==0) {
			chk(f[i][j][0],f[i][j][1]), f[i][j][1]=inf;
			chk(f[i+1][j][0],f[i][j][0]);
			chk(f[i][j+1][0],f[i][j][0]);
		}
		else {
			chk(f[i][j][1],f[i][j][0]+1), f[i][j][0]=inf;
			chk(f[i+1][j][1],f[i][j][1]);
			chk(f[i][j+1][1],f[i][j][1]);
		}
	}
	cout << min(f[n][m][0],f[n][m][1]) << '\n';
	return 0;
}

清扫

看看怎么合并,设 \(res_u\) 表示子树 \(u\) 根向上延伸多少个,\(mat_u\) 表示 lca 为 \(u\) 的链有多少个。那么可以发现有关系 \(mat_u=\sum_{v\in son_u} res_v-a_u\),最后更新 \(res_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=100005;

int T, n, a[N], flag=1, res[N], mat[N];
vector<int> to[N];

void dfs(int x,int fad) {
	if(to[x].size()==1) return res[x]=a[x], void();
	for(int y:to[x]) if(y!=fad) dfs(y,x), res[x]+=res[y];
	mat[x]=res[x]-a[x];
	if(mat[x]<0) flag=0;
	res[x]-=2*mat[x];
}

void mian() {
	cin >> n;
	up(i,1,n) cin >> a[i];
	if(n==1) {
		cout << "NO\n";
		return;
	}
	if(n==2) {
		cin >> n; cin >> n;
		if(a[1]==a[2]) cout << "YES\n";
		else cout << "NO\n";
		return;
	}
	up(i,2,n) {
		int u, v;
		cin >> u >> v;
		to[u].pb(v);
		to[v].pb(u);
	}
	up(i,1,n) if(to[i].size()>1) {
		flag=1, dfs(i,0);
		if(!flag||res[i]) cout << "NO\n";
		else cout << "YES\n";
		break;
	}
	up(i,1,n) res[i]=mat[i]=0, to[i].clear();
}

signed main() {
	freopen("clean.in","r",stdin);
	freopen("clean.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> T;
	while(T--) mian();
	return 0;
}

定向

[题解 ARC092F] Two Faced Edges - 洛谷专栏 (luogu.com.cn)

感觉把最短路那里看成 只有什么情况 \(dis=1\),更自然一点。

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

int n, m, u[M], v[M], f[N][N], g[N][N];
int stk[N], top, vis[N], gp[N], cnt;
vector<int> F[N], G[N];

void dfs1(int x) {
	for(int y:F[x]) {
		if(vis[y]) continue;
		vis[y]=1, dfs1(y);
	}
	stk[++top]=x;
}

void dfs2(int x) {
	for(int y:G[x]) {
		if(gp[y]) continue;
		gp[y]=cnt, dfs2(y);
	}
}

void dfs(int dis[],int x) {
	for(int y:F[x]) {
		if(dis[y]) continue;
		dis[y]=dis[x]+1;
		dfs(dis,y);
	}
}

void solve(int dis[],int s) {
	dis[s]=1, dfs(dis,s);
	up(i,1,n) --dis[i];
}

signed main() {
	freopen("direct.in","r",stdin);
	freopen("direct.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m;
	up(i,1,m) {
		cin >> u[i] >> v[i];
		F[u[i]].pb(v[i]);
		G[v[i]].pb(u[i]);
	}
	up(i,1,n) if(!vis[i]) vis[i]=1, dfs1(i);
	dn(u,n,1) {
		int i=stk[u];
		if(gp[i]) continue;
		gp[i]=++cnt, dfs2(i);
	}
	up(i,1,n) solve(f[i],i);
	up(i,1,n) reverse(F[i].begin(),F[i].end());
	up(i,1,n) solve(g[i],i);
	up(i,1,m) {
		int x=u[i], y=v[i];
		cout << ((gp[x]==gp[y])^(f[x][y]>1||g[x][y]>1));
	}
	return 0;
}

染色

场上就差一点点了,因为 lgjoj 上面是先蓝所以我不保证我下面颜色写对了。

首先直接 dp 真正的形态显得过于复杂了,考虑把颜色段提出来,最后乘上组合数就好了。现在你只要考虑由单个 \(\text{w}\) 隔开的 \(\text{rb}\) 交替段形成的颜色序列有哪些,长度是什么。

现在我们要考虑什么样的颜色段是合法的,又因为 dp 最终序列数你要想一种不会掉可行性的唯一构造方法。

最终会因为 \(\text{w}\) 形成若干段,我们考虑一个连续段怎么操作,第一次操作只能染 \(\text{r}\),第二次操作只能染 \(\text{b}\),之后不管用 \(\text{r/b}\) 都可以做到 \(\text{rb}\) 分别多一个,感觉三次及以后的操作 \(\text{r/b}\) 等价。试试具体的,只有 \(\text{r,r...r,b...b,r...b,b...r}\) 这些情况,发现额外次数其实都是 \(cnt(\text{b})-1\),不知道需不需要补充的,你可以从左到右操作。

现在考虑多个段怎么做呀,显然是,拿到的 \(\text{r}\) 优先给第一次操作,拿到的 \(\text{b}\) 优先拿给第二次操作,然后空余的操作如果可以的话就拿来做额外操作。发现这里让人不任意的点在于,可能会有 \(\text{str=rbrrrb}\) 这种你的额外操作大多只能打在第一个 \(\text{b}\) 为第二次操作的那个段上,所以你的构造方案应该是按照 \(cnt(b)\downarrow\) 去做额外操作的。

填写式的可重集方案数不容易做,但是题目限制颇多,\(cnt(b)\downarrow\) 比划分数小是可以 dfs 出来的,嗯疑似不止这个题目这么处理,那么我们现在可以考虑一下一个 \(cnt(b)\downarrow\) 的序列的贡献。首先有一个可重集排列的方案数乘进去,然后发现剩下的问题是把 \(n\) 填进若干非空段和若干可空段。

强制非空的:\(m-1\) 个整段间的 \(\text{w}\)\(cnt(b)=0\) 的一个 \(\text{r}\)\(cnt(b)>1\) 的最左右 \(\text{b}\) 夹着的 \(2cnt(b)-1\) 个段。

可以空的:整段最左/右的 \(\text{w}\)\(cnt(b)>1\) 的最左右边的 \(\text{r}\)

考虑前者 \(l\) 个,后者 \(r\) 个,那么等价于 \(n+r\) 个放进 \(l+r\) 个强制非空的桶,方案数 \(\binom{n+r-1}{l+r-1}\),完结了喵。

#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, P=1e9+7;

int n, k, ans, a[N], mul[N], inv[N], riv[N]; 
char str[N];

int C(int n,int m) {
	if(m<0||n<m) return 0;
	return mul[n]*riv[m]%P*riv[n-m]%P;
}

inline void add(int &a,int b) { a=(a+b)%P; }

void solve(int m) {
	if(!m) return ++ans, void();
	int i=0, j=0, af=0;
	up(u,1,k) {
		if(str[u]=='r') {
			if(i<m) ++i;
			else if(af>0) --af;
		}
		else {
			if(j<i&&a[j+1]) af+=a[++j]-1;
			else if(af>0) --af;
		}
	}
	if(i<m||j<m&&a[j+1]||af) return;
	int cnt=m-1, res=2;
	up(i,1,m) {
		if(!a[i]) ++cnt;
		else res+=2, cnt+=2*a[i]-1;
	}
	int val=C(n+res-1,cnt+res-1);
	for(int l=1, r=1; l<=m; l=r+1, r=l) {
		while(r<m&&a[r+1]==a[l]) ++r;
		val=val*C(m-l+1,r-l+1)%P;
	}
	(ans+=val)%=P;
}

void dfs(int m,int sum) {
	if(m-1+sum>n) return;
	solve(m);
	dn(v,((m==0)?n:a[m]),0) a[m+1]=v, dfs(m+1,sum+(v?(2*v-1):1));
}

signed main() {
	freopen("color.in","r",stdin);
	freopen("color.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	mul[0]=inv[0]=inv[1]=riv[0]=1;
	up(i,1,1000) mul[i]=mul[i-1]*i%P;
	up(i,2,1000) inv[i]=inv[P%i]*(P-P/i)%P;
	up(i,1,1000) riv[i]=riv[i-1]*inv[i]%P;
	cin >> n >> k >> (str+1);
	dfs(0,0);
	cout << (ans%P+P)%P << '\n';
	return 0;
}
posted @ 2025-11-25 20:19  Hypoxia571  阅读(14)  评论(0)    收藏  举报