如果这就是人类脑海的话 雪白纸上划出血红层层痕迹 不如杀死这些记忆

test24

超级集合super

正序不太好考虑,考虑一下倒序怎么做。假设当前的位置为 \(x\),我们来考虑上一轮的位置是什么,设 \(c(y)\) 表示上一轮 \(<y\)\(a_k\) 个数即编号 \(y\) 向本轮的减少量,应该有 \((x+c(v)=y) \in (a_{c(v)},a_{c(v)+1})\)。注意到 \(c(y)\) 单调不降,特判 \(a_1>1\),顺序枚举 \(i=c(v)\) 考要做 \(\min(res,\lfloor\frac{(a[i+1]-1-ans}{i}\rfloor)\) 次即可。

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

int id, T, n, k, a[N], ans;

void mian() {
	cin >> n >> k, ans=1;
	up(i,1,n) cin >> a[i];
	if(a[1]!=1) { cout << 1 << '\n'; return; }
	up(i,1,n-1) {
		int t=min((a[i+1]-1-ans)/i,k);
		k-=t, ans+=i*t;
	}
	ans+=k*n;
	cout << ans << '\n';
}

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

宝可梦pokemon

这个永久增加看着很烦,但是发现假设存在一个雇佣子序列 \(a\to b\to c\) 满足 \(b/c\) 都是用 \(j\) 打败 \(a/b\) 的,那么直接让 \(c\) 打败 \(a\) 也是可以的,所以我们认为属性并非永久增加求出的答案是一样的。

考虑一下 \(y\)\(j\) 打败 \(i\) 怎么转移。\(a_{y,j}>a_{x,j}\),费用 \(c_y\)\(a_{y,j}<a_{x,j}\),费用 \(c_y+a_{x,j}-a_{y,j}\)

感觉是一个轮回形态的转移,普通 dp 比较难了,考虑简图跑最短路做 dp。先对于每一种属性值排序(大到小为例),然后转移有向前缀和向后缀两种,非常经典的建立两条辅助链,连接进入前缀贡献 \(a_{x,j}\) 和离开前缀贡献 \(c_y-a_{y,j}\),进入后缀贡献 \(0\) 离开后缀贡献 \(c_y\)

自我欺骗这一块,不要有笨蛋觉得建了图边数点数对就可以随便最短路惹,不难发现上述东西跟暴力转移的区别极其有限,spfa 就是在这种网格图上跑的最慢的。我们希望钦定边权非负这样子可以跑复杂度绝对正确的 dij,希望提高 \(c_y-a_{y,j}\) 和降低 \(a_{x,j}\) 来达成这个目的,考虑 \(c-a\) 的最小值增加起来,对应的前缀直接减仍然非负,但是后缀怎么办,不妨考虑一段段递归这样子加,最后再段间补上 \(a-c\) 的非负差。

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

int id, j, T, n, m, tot, c[N], dis[N], pos[N], tag[N], stk[N], top, val[N];
vector<int> a[N];
vector<pii> to[N];
priority_queue<pii> q;

inline void eadd(int u,int v,int w) {
	to[u].pb(mp(v,w));
} 

void mian() {
	cin >> n >> m, tot=n;
	up(i,1,n) cin >> c[i];
	up(i,1,n) {
		a[i].resize(m+1);
		up(j,1,m) cin >> a[i][j]; 
	}
	up(i,1,n) pos[i]=i;
	for(j=1; j<=m; ++j) {
		sort(pos+1,pos+1+n,[](int A,int B){return a[A][j]<a[B][j];});
		up(i,1,n) val[i]=a[pos[i]][j]-c[pos[i]];
		stk[top=1]=n+1;
		dn(i,n,1) {
			while(top>1&&val[i]>val[stk[top]]) --top;
			stk[++top]=i;
		}
		dn(g,top,2) {
			int pre=stk[g+1], l=stk[g], r=stk[g-1]-1;
			if(l>1) eadd(tot+1,tot,val[l]-val[pre]);
			up(i,l,r) {
				++tot;
				int u=pos[i];
				if(i>l) eadd(tot,tot-1,0);
				eadd(u,tot,a[u][j]-val[l]);
				eadd(tot,u,val[l]-val[i]);
			}
		}
		dn(i,n,1) {
			++tot;
			int u=pos[i];
			eadd(u,tot,0);
			eadd(tot,u,c[u]);
			if(i<n) eadd(tot,tot-1,0);
		}
	}
	memset(dis,0x3f,sizeof(dis));
	dis[1]=0, q.push(mp(0,1));
	while(q.size()) {
		int x=q.top().second; q.pop();
		if(tag[x]) continue;
		tag[x]=1;
		for(pii p:to[x]) {
			int y=p.first, w=p.second;
			if(dis[x]+w<dis[y]) {
				dis[y]=dis[x]+w;
				q.push(mp(-dis[y],y));
			}
		}
	}
	cout << dis[n] << '\n';
	up(i,1,tot) a[i].clear(), to[i].clear(), tag[i]=0;
}

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

城市地铁

先考虑 \(lca(a,b)=b\) 的怎么做,显然是在每个点都尽可能往上跳即可。预处理每个点最上可以跳到哪里,可以排序+并查集来做,然后预处理一个倍增数组就能动态求了。

现在考虑 \(lca(a,b)=p\) 的怎么做,分两种情况,在 \(p\) 换乘地铁,把链拆开就能算了,或者找到一个经过 \(p\) (进一步说 \(lca=p\))的地铁 \((x,y)\)\(a\to (l=x,\dots,p),b\to (r=y,\dots,p)\)\(l\to r\),首先 \(l,r\) 一定可以选尽可能调到不必 \(p\) 浅的地方,像链一样处理贡献之后问题变成了对于 \((l,r)\) 是否存在 \((x,y)\) 满足 \(x/y\) 分别在 \(l/r\) 的子树里面,刻画子树里面就是 \(dfn\) 在某个区间,问题等价于二位数点,注意带着脑子写代码。

#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=400005, M=23;

int id, n, m, q, cw, T, L[N], R[N], stamp, fa[N][M], dep[N], tag[N];
int X[N], Y[N], Ans[N], cnt[N], dsu[N], lnk[N], g[N][M], tr[N];
pii wu[N], len[N];
vector<int> to[N];
vector<pii> Q[N];

void dfs(int x,int fad) {
	L[x]=++stamp, dep[x]=dep[fad]+1, fa[x][0]=fad;
	up(i,1,T) fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int y:to[x]) if(y!=fad) dfs(y,x);
	R[x]=stamp, dsu[x]=x;
}

inline int lca(int x,int y) {
	if(dep[x]<dep[y]) 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 void add(int x,int v) { for( ; x<=n; x+=x&-x) tr[x]+=v; }
inline int ask(int x) { int ret=0; for( ; x; x-=x&-x) ret+=tr[x]; return ret; }
int get(int x) { return x==dsu[x]?x:dsu[x]=get(dsu[x]); }

signed main() {
	freopen("metro.in","r",stdin);
	freopen("metro.out","w",stdout); 
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> id >> n, T=log2(n);
	up(i,2,n) {
		int u, v;
		cin >> u >> v;
		to[u].pb(v);
		to[v].pb(u);
	}
	dfs(1,0);
	cin >> m;
	up(i,1,m) {
		int x, y;
		cin >> x >> y;
		int p=lca(x,y);
		wu[++cw]=mp(p,x);
		wu[++cw]=mp(p,y);
		if(L[y]<L[x]) swap(x,y);
		len[i]=mp(x,y);
	}
	sort(wu+1,wu+1+cw,[](pii i,pii j){return dep[i.first]<dep[j.first];});
	up(u,1,cw) {
		for(int i=wu[u].second; ; ) {
			if(!(i=get(i))) break;
			if(dep[i]<dep[wu[u].first]) break; 
			lnk[i]=wu[u].first, dsu[i]=get(fa[i][0]);
		}
	}
	up(i,1,n) g[i][0]=lnk[i]?lnk[i]:i;
	up(j,1,T) up(i,1,n) g[i][j]=g[g[i][j-1]][j-1];
	cin >> q;
	up(i,1,q) {
		int x, y;
		cin >> x >> y;
		if(x==y) { Ans[i]=0; continue; }
		int p=lca(x,y);
		if(p==x) swap(x,y);
		if(p==y) {
			dn(j,T,0) if(dep[g[x][j]]>dep[p]) x=g[x][j], Ans[i]+=(1<<j);
			if(dep[g[x][0]]>dep[p]) Ans[i]=-1; else ++Ans[i];
			continue;
		}
		dn(j,T,0) if(dep[g[x][j]]>dep[p]) x=g[x][j], Ans[i]+=(1<<j);
		dn(j,T,0) if(dep[g[y][j]]>dep[p]) y=g[y][j], Ans[i]+=(1<<j);
		if(dep[g[x][0]]>dep[p]||dep[g[y][0]]>dep[p]) Ans[i]=-1;
		else {
			tag[i]=1;
			if(L[y]<L[x]) swap(x,y);
			Q[L[x]-1].pb(mp(-i,y));
			Q[R[x]  ].pb(mp( i,y));
		}
	}
	sort(len+1,len+1+m,[](pii i,pii j){return L[i.first]<L[j.first];});
	int j=1;
	up(i,1,n) {
		while(j<=m&&L[len[j].first]<=i) {
			add(L[len[j].second],1);
			++j;
		}
		for(pii p:Q[i]) {
			int id=p.first, x=p.second;
			if(id>0) cnt[+id]+=ask(R[x])-ask(L[x]-1);
			if(id<0) cnt[-id]-=ask(R[x])-ask(L[x]-1);
		}
	}
	up(i,1,q) {
		if(tag[i]) cout << Ans[i]+2-(cnt[i]>0) << '\n';
		else cout << Ans[i] << '\n';
	}
	return 0;
}

寿司晚宴sushi

关心吃寿司的喜爱排名位置 \(\{C_i\},\{A_i\},\{B_i\}\),首先要求 \(\{a_{A_i}\},\{b_{B_i}\},\{c_{C_i}\}\)\(\{1,\dots,n\}\) 的划分,然后要求 \(a_{A_i},b_{B_i}\)\(c\) 中比 \(c_{C_i}\) 出现的晚,且 \(c_{C_{i}}\)\(c_{C_{i+1}}\) 出现的早,对小A小B同理。这个限制像是经典问题诶,在 \(c\) 里面 \(a_{A_i’(\geq i)},b_{B_i'(\geq i)},c_{C_i'(>i)}\) 肯定都在 \(c_{C_i}\) 的后面就够了,考虑倒着插 \(c_{C_i}\) 肯定放在第一个位置,对应的 \(a_{A_i}/b_{B_i}\) 是随便找个后面的位置放就好了。也就是说对于一个合法的 \(\{c_{C_i}\}\),其对应着 \(\sum_{i=1}^{n/3} (3i-2)(3i-1)\)\(c\),之后只用考虑 \(\{c_{C_i}\}\) 的方案数了。

吃寿司的有序序列分别为 \(\{a_{A_i}\},\{b_{B_i}\},\{c_{C_i}\}\),考虑确定 \(\{a_{A_i}\},\{b_{B_i}\}\) 的前提刻画一下 \(c\) 的条件和方案数量。

  • \(\{a_{A_i}\},\{b_{B_i}\},\{c_{C_i}\}\)\(\{1,\dots,n\}\) 的划分
  • \(c_{C_i}\neq a_{j(\leq A_i)},c_{C_i}\neq b_{j (\leq B_i)}\),对小 A 小 B 同理。
  • 进一步考虑怎么计算 \(c\) 的方案数,考虑 \(c_{C_i}\) 选什么,\(i\) 越大限制严格严格,从大到小填, \(a_{1\to A_i},b_{1\to B_i}\) 不能选,\(j>i,a_{A_j},b_{B_j},c_{C_j}\) 不能选,那么方案数就是 \(\sum_{i=1}^{n/3} (3i-cnt_{A_i,B_i})\),其中 \(cnt_{i,j}\) 表示 \(a_{1\to i},b_{1\to j}\) 的并集大小。

之后带着系数(\(c\) 的方案数)去 dp 合法的 \(\{a_{A_i}\},\{b_{B_i}\}\) 方案数即可。设 \(f_{t,i,j}\) 表示考虑到 \(t\) 轮,\(A_{t}=i,B_t=j\) 的带权方案数量,转移是容易的,要写一个前缀和优化。

#include<bits/stdc++.h>
#define ll 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=505, P=1e9+7;

int id, T, n, a[N], b[N], tag[N], sub[N][N], ca[N][N], cb[N][N], f[N][N][N];

void mian() {
	memset(tag,0,sizeof(tag));
	memset(sub,0,sizeof(sub));
	memset(ca,0,sizeof(ca));
	memset(cb,0,sizeof(cb));
	memset(f,0,sizeof(f));
	cin >> n;
	up(i,1,n) cin >> a[i];
	up(i,1,n) cin >> b[i];
	up(i,1,n) {
		tag[a[i]]=1, sub[i][0]=i;
		up(j,1,n) sub[i][j]=sub[i][j-1]+(!tag[b[j]]);
	}
	up(i,1,n) up(j,1,i) ca[i][a[j]]=1, cb[i][b[j]]=1;
	up(i,0,n) up(j,0,n) f[0][i][j]=1;
	up(t,1,n/3) up(i,1,n) up(j,1,n) {
		if(!cb[j][a[i]]&&!ca[i][b[j]]) f[t][i][j]=(ll)f[t-1][i-1][j-1]*(3*t-sub[i][j])%P;
		f[t][i][j]=((((ll)f[t][i][j]+f[t][i-1][j])%P+f[t][i][j-1])%P-f[t][i-1][j-1])%P;
	}
	up(i,1,n/3) f[n/3][n][n]=(ll)f[n/3][n][n]*(3*i-2)%P*(3*i-1)%P;
	cout << (f[n/3][n][n]+P)%P << '\n';
}

signed main() {
	freopen("sushi.in","r",stdin);
	freopen("sushi.out","w",stdout); 
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> id >> T;
	while(T--) mian();
	return 0;
}
posted @ 2025-10-19 21:01  Hypoxia571  阅读(6)  评论(0)    收藏  举报