朝圣显像 不及那人将门扉轻轻叩响 欢迎来到我的城市 嗅玫瑰绽放

test13

魔法 magic

首先 \(g\) 可以只通过 \(s_i=s_{i+1}=1\) 的数量和 \(s_1/s_{n+m}\) 的取值确定。

不管首尾只考虑第一个条件,容易得到一个做法,设第 \(i\)\(1\)\(l_i/r_i\),那么第 \(i\)\(1\)\(s3_{\min\{l_i,r_i\}\to\max\{l_i,r_i\}}\),考虑能不能贴到 \(s_3\) 中的第 \(i-1\)\(1\),能就贴,不能贴就放到能放的最右边。不难发现因为串的性质不会因为贪心导致后续不合法、且贪心可以求出最优解法。

第二个限制只要强制 \(l_1,r_1\) 的选取在 \(s_3\) 的首位即可,右边贪心自然最优。

#include<bits/stdc++.h>
#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=600005;

int n, m, cnt, len, L[N], R[N], s[N], t[N], l[N], r[N], lst, ans[N], Ans, Pluto;
char str[N];

inline void read(int arr[]) {
	cin >> (str+1);
	int sl=strlen(str+1);
	if(sl==n+m-1) {
		cout << 14 << '\n';
		exit(0);
	}  // lgxq 要求的
	up(i,1,len) arr[i]=(str[i]-'0');
}

void solve() {
//	cout << "s "; up(i,1,m) cout << l[i] << ' '; cout << '\n';
//	cout << "t "; up(i,1,m) cout << r[i] << ' '; cout << '\n';
	Ans=0; up(i,1,len) ans[i]=0; 
	lst=max(l[1],r[1]), ans[lst]=1; 
	up(i,2,m) {
		if(lst+1>=min(l[i],r[i])) ans[++lst]=1;
		else ans[lst=max(l[i],r[i])]=1;
	}
	up(i,2,len) Ans+=(ans[i-1]==ans[i]);
//	cout << "w " << Ans << " : "; up(i,1,len) cout << ans[i]; cout << '\n';
	Pluto=max(Pluto,Ans);
}

inline void copy() { up(i,1,m) l[i]=L[i], r[i]=R[i]; }

inline void su1() { l[1]=1; } // r[1]==1 
inline void sd1() { r[1]=1; } // l[1]==1
inline void tu1() { l[m]=len; } // r[len]==1 
inline void td1() { r[m]=len; } // l[len]==1 
inline void su2() { up(i,1,m) { if(l[i]!=l[i-1]) break; ++l[i]; } } // r[1]==0
inline void sd2() { up(i,1,m) { if(r[i]!=r[i-1]) break; ++r[i]; } } // l[1]==0
inline void tu2() { dn(i,m,1) { if(l[i]!=l[i+1]) break; --l[i]; } } // r[len]==0
inline void td2() { dn(i,m,1) { if(r[i]!=r[i+1]) break; --r[i]; } } // l[len]==0


signed main() {
//	freopen("data.txt","r",stdin);
//	freopen("magic3.in","r",stdin);
	freopen("magic.in","r",stdin);
	freopen("magic.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m, len=n+m, read(s), read(t);
	up(i,1,len) if(s[i]) L[++cnt]=i;
	dn(i,len,1) if(t[i]) R[cnt--]=i;
	if(s[1]==1&&s[len]==1) copy(), sd1(), td1(), solve();
	if(s[1]==1&&s[len]==0) copy(), sd1(), td2(), solve();
	if(s[1]==1&&t[len]==1) copy(), sd1(), tu1(), solve();
	if(s[1]==1&&t[len]==0) copy(), sd1(), tu2(), solve();
	if(s[1]==0&&s[len]==1) copy(), sd2(), td1(), solve();
	if(s[1]==0&&s[len]==0) copy(), sd2(), td2(), solve();
	if(s[1]==0&&t[len]==1) copy(), sd2(), tu1(), solve();
	if(s[1]==0&&t[len]==0) copy(), sd2(), tu2(), solve();
	if(t[1]==1&&s[len]==1) copy(), su1(), td1(), solve();
	if(t[1]==1&&s[len]==0) copy(), su1(), td2(), solve();
	if(t[1]==1&&t[len]==1) copy(), su1(), tu1(), solve();
	if(t[1]==1&&t[len]==0) copy(), su1(), tu2(), solve();
	if(t[1]==0&&s[len]==1) copy(), su2(), td1(), solve();
	if(t[1]==0&&s[len]==0) copy(), su2(), td2(), solve();
	if(t[1]==0&&t[len]==1) copy(), su2(), tu1(), solve();
	if(t[1]==0&&t[len]==0) copy(), su2(), tu2(), solve();
	cout << Pluto << '\n';
	return 0;
} 

游戏game

暴力就是按照题意每次取可以取的最大值,问题是暴力带 \(\log\),但是数据范围看着想要我们对于每一个询问线性求解。

那么不能用堆,因为不考虑一放进就取的数那么取的数不增,不妨预处理排序,指针处理现在非即放即取的数取到 \(v\),双指针一下就好了。新加入的显然 \(>v\) 直接取不然塞到桶里面。

#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 n, m, T, p, a[N], sp[N], cnt[N], v[N], tot, j, Ans;

signed main() {
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> T;
	up(i,1,n) cin >> a[i], sp[i]=a[i];
	sort(sp+1,sp+1+n), m=unique(sp+1,sp+1+n)-sp-1;
	up(i,1,n) a[i]=lower_bound(sp+1,sp+1+m,a[i])-sp;
	while(T--) {
		cin >> p, tot=Ans=0, j=m;
		up(i,1,p-1) ++cnt[a[i]];
		up(i,p,n) {
			if(a[i]>j) v[++tot]=sp[a[i]];
			else {
				++cnt[a[i]];
				while(j&&!cnt[j]) --j;
				--cnt[j], v[++tot]=sp[j];
			}
		}
		while(j>0) {
			while(cnt[j]) --cnt[j], v[++tot]=sp[j];
			--j;
		}
		for(int i=1; i<=n; i+=2) Ans+=v[i];
		cout << Ans << '\n';
	}
	return 0;
} 

最短路path

我感觉这题我需要深刻地进行自我反思,而做法是好表述的没有赘述的必要,问题在于我的状态设计为什么因为看错题题目(?)而固化。

思考一下怎么描述一个考虑 \(1,\dots,i\) 的状态,首先 \(dis_i\) 是必要的,然后想要转移到别的颜色需要知道前一个不同归属点的 \(dis\),需要设计 \(f_{i,l,r}\) 表示考虑 \(1,\dots,i\) 到最后一个 \(A/B\) 的最短路为 \(l/r\) 的方案数,是好转移的。

手搓一下可以发现连续段没什么用其实,设 \(s_{l,\dots,r}\) 归属相同那么最优解法肯定可以只经过 \(s_l,s_r\),进而可以发现上述的 \(l/r\) 其实应该满足 \(|l-r|\leq 2\)。实际操作 dp 中可能会出现的 \(ABBB...\) 这种可能导致 \(r-l>2\) 的显然可以令 \(r=l+2\),不影响最终的 \(\min\{l,r\}+1\)

#include<bits/stdc++.h>
#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=4005, D=3, P=1e9+7;

int n, m, f[N][N][5][2], Ans, fa, fb;
char s[N];

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

signed main() {
	freopen("path.in","r",stdin);
	freopen("path.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m >> (s+1);
	if(s[1]=='?'||s[1]=='A') f[1][1][D-1][0]=1;
	if(s[1]=='?'||s[1]=='B') f[1][0][D+1][1]=1;
	up(i,1,n-1) {
		up(j,0,m+1) up(dk,-2,2) {
			int k=j+dk;
			if(k<0||k>m+1) continue;
			if(s[i+1]=='?'||s[i+1]=='A') {
				add(f[i+1][min(j+1,k+2)][D+k-min(j+1,k+2)][0],f[i][j][D+dk][0]);
				add(f[i+1][min(j,k)+1][D+min(j+2,k)-min(j,k)-1][0],f[i][j][D+dk][1]);
			}
			if(s[i+1]=='?'||s[i+1]=='B') {
				add(f[i+1][j][D+min(j+2,k+1)-j][1],f[i][j][D+dk][1]);
				add(f[i+1][min(j,k+2)][D+min(j,k)+1-min(j,k+2)][1],f[i][j][D+dk][0]);
			}
		}
	}
	up(j,0,m+1) up(dk,-2,2) {
		int k=j+dk;
		if(k<0||k>m+1) continue;
		if(min(j,k)+1<=m) add(Ans,(f[n][j][D+dk][0]+f[n][j][D+dk][1])%P);
	}
//	up(i,1,n) up(j,0,m+1) up(dk,-2,2) {
//		int k=j+dk;
//		if(k<0||k>m+1) continue;
//		if(f[i][j][D+dk][0]) cout << "f " << i << ' ' << j << ' ' << k << ' ' << 0 << " = " << f[i][j][D+dk][0] << '\n';
//		if(f[i][j][D+dk][1]) cout << "f " << i << ' ' << j << ' ' << k << ' ' << 1 << " = " << f[i][j][D+dk][1] << '\n';
//	}
	cout << (Ans%P+P)%P << '\n';
	return 0;
}

梦魇nightmare

原题 P9058/P9678,感觉前置题目 P5926 比这个难是可以说的吗,anyway 先讲讲前置题目做法。

有贡献(即不被三维偏序)的点对应该是不多的,我们考虑什么样的点对 \((i,q)\) 是有贡献的,设 \((i,p(<q))\) 有贡献、\(a_i\leq a_q\),首先 \(a_p\geq a_q\) 否则 \((i,p)\) 偏序 \((i,q)\)。之后要满足 \(zz\) 画一下数轴就是要求 \(a_q<\frac{a_i+a_p}{2}\),那么一定在数轴上不断至少减半(\(a_{p/q}-a_i\)),所以可能有贡献的点对级别不超过 \(O(n\log n)\)

现在考虑一下怎么求这个东西,对于 \((i,p)\)\(\min q\),要求 \(q>p,a_i\leq a_q<\frac{a_i+a_p}{2}\),不妨离散化以后面的限制为下标建立线段树,前面的要求是递增的排序加入,写一个线段树二分就能算出答案。询问是查询 \(L\leq l,r\leq R\)\(\min v\),二维偏序即可。

回到这个题目,想要先从树上扣下来不妨考虑点分治,然后多加了两次 \(root\to lca\) 显然不影响答案。考虑 \((i,j)\) 有贡献的条件显然是 \(a_i,a_j\) 严格是 \([i,j]\) 的最小和次小值,对于 \(i\) 找右边第一个更大/更小的即可,必须使用单调栈维护因为原题卡常,总共有 \(O(n\log n)\) 个点对。查询跟前置题目一摸一样(

#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 ls(p) (p<<1)
#define rs(p) (p<<1|1)

using namespace std;

inline int read() {
    int X=0; bool flag=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') flag=0; ch=getchar(); }
    while(ch>='0'&&ch<='9') { X=(X<<1)+(X<<3)+ch-'0'; ch=getchar(); }
    if(flag) return X;
    return ~(X-1);
}

const int N=200005, M=7000005, Q=1000005;
const ll inf=1e18;

int n, m, T, len, siz[N], val[N], tag[N], st[N], top;
int hd[N], nxt[N<<1], eg[N<<1], to[N<<1], ovo, dis[N];
ll f[N], Ans[Q], bit[N];

struct node {
	int l, r; ll v;
	node() {}
	node(ll L,ll R,ll V) { l=L, r=R, v=V; }
	bool operator<(const node &rhs) const { return r<rhs.r; }
} p[M], q[Q];

void add(int x,ll v) {
	x=n-x+1;
	for( ; x<=n; x+=x&-x) bit[x]=min(bit[x],v); 
}

ll ask(int x) {
	x=n-x+1;
	ll res=inf;
	for( ; x; x-=x&-x) res=min(res,bit[x]);
	return res;
}

inline void eadd(int u,int v,int w) {
	nxt[++ovo]=hd[u], hd[u]=ovo, eg[ovo]=w, to[ovo]=v;
}

void dp(int x,int fad=0) {
	siz[x]=1, val[x]=0;
	int res=0;
	for(int i=hd[x]; i; i=nxt[i]) {
		int y=to[i];
		if(tag[y]||y==fad) continue;
		dp(y,x), siz[x]+=siz[y], val[x]=max(val[x],siz[y]);
	}
	dis[++len]=x;
}

void DP(int x,int fad=0) {
	for(int i=hd[x]; i; i=nxt[i]) {
		int y=to[i], w=eg[i];
		if(tag[y]||y==fad) continue;
		f[y]=f[x]+w, DP(y,x);
	}
}

void div(int x) {
	
	len=0, dp(x);
	up(u,1,len) {
		int i=dis[u];
		val[i]=max(val[i],len-siz[i]);
		if(val[i]<val[x]) x=i;
	}
	f[x]=0, DP(x);
	
	sort(dis+1,dis+1+len);
	
//	up(i,1,len) cout << dis[i] << ' ' << f[dis[i]] << '\n';
	
	top=0;
	dn(u,len,1) {
		int i=dis[u];
		while(top&&f[i]<f[st[top]]) --top;
		if(top) {
			int j=st[top];
			p[++m]=node(i,j,f[i]+f[j]);
		}
		st[++top]=i;
	}
	top=0;
	up(u,1,len) {
		int i=dis[u];
		while(top&&f[i]<f[st[top]]) --top;
		if(top) {
			int j=st[top];
			p[++m]=node(j,i,f[i]+f[j]);
		}
		st[++top]=i;
	}
	
	tag[x]=1;
	for(int i=hd[x]; i; i=nxt[i]) {
		int y=to[i];
		if(!tag[y]) div(y);
	}
}

signed main() {
//	freopen("1.txt","r",stdin)
	freopen("nightmare.in","r",stdin);
	freopen("nightmare.out","w",stdout);
	
	n=read();
	up(i,2,n) {
		int u=read(), v=read(), w=read();
		eadd(u,v,w), eadd(v,u,w);
	}
	div(1);
	T=read();
	up(i,1,T) {
		int l=read(), r=read();
		q[i]=node(l,r,i);
	}
	sort(p+1,p+1+m);
	sort(q+1,q+1+T);
	up(i,0,n+1) bit[i]=inf; 
	int j=1;
	up(u,1,T) {
		int l=q[u].l, r=q[u].r;
		while(j<=m&&p[j].r<=r) add(p[j].l,p[j].v), ++j;
		Ans[q[u].v]=ask(l);
	} 
	up(i,1,T) printf("%lld\n", (Ans[i]>=inf)?-1:Ans[i]);
	return 0;
}
posted @ 2025-10-09 22:11  Hypoxia571  阅读(9)  评论(0)    收藏  举报