像人偶 执着于东拼西凑 用尽了所有计谋 封不住一个缺口 不肯走

test7

病毒 virus

考虑 \((day,dis)\) 二元组,前者表示天数后者表示据上一个起点的距离,不难发现按这个排序最小的肯定是不劣的因为 \((day,dis)\) 可以成为 \((day+1,0)\)。直接对这个做最短路即可,需要额外对 \(w_{i\to j}\) 查找 \(>T\) 的时间里面最早的可以离开的时间,st 标推下去即可。

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

int n, m, t, k, d, lim[N], day[N], pre[N], f[N][M];
vector<pii> to[N];
queue<int> q;

int query(int l,int w) {
	dn(i,t,0) if(l+(1<<i)-1<=d-1&&f[l][i]<w) l+=(1<<i);
	return l;
}

signed main() {
	freopen("virus.in","r",stdin);
	freopen("virus.out","w",stdout); 
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m;
	cin >> k;
	cin >> d, t=log2(d);
	while(m--) {
		int u, v, w;
		cin >> u >> v >> w;
		to[u].pb(mp(v,w));
		to[v].pb(mp(u,w));
	}
	memset(day,0x3f,sizeof(day));
	while(k--) { int i; cin >> i; day[i]=pre[i]=0, q.push(i); }
	up(i,0,d-1) cin >> lim[i], f[i][0]=lim[i];
	up(j,1,t) up(i,0,d-1) if(i+(1<<j)-1<=d-1) f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);
	while(q.size()) {
		int x=q.front();
		q.pop();
		for(pii p:to[x]) {
			int y=p.first, w=p.second;
			if(pre[x]+w<=lim[day[x]]) {
				if(day[x]>day[y]||day[x]==day[y]&&pre[x]+w>=pre[y]) continue;
				day[y]=day[x], pre[y]=pre[x]+w, q.push(y);
			}
			else {
				int now=query(day[x]+1,w);
				if(now>d||now>day[y]||now==day[y]&&w>=pre[y]) continue;
				day[y]=now, pre[y]=w, q.push(y);
			}
		}
	}  
	up(i,1,n) {
		int val=(day[i]+(bool)pre[i]<=d)?(day[i]+(bool)pre[i]):-1;
		cout << val << ' ';
	}
	return 0;
}

浪潮sao

出成 \(n=5000\) 然后给全部询问的答案真的很有误导性,不懂为社么不出成交互题 /fad

首先可以通过 \(Q[1,i],Q[i,n]\) 求第一个/最后一个 \(\text{S}\) 的位置 \(l,r\)

直觉 \(S\) 很有可能在前缀/后缀是严格众数,考虑 \(i\) 的合法性,若 \(Q[l,i]=Q[l,i-1]+1=Q[l+1,i]+1\) 或者 \(Q[i,r]=Q[i+1,r]+1=Q[i,r-1]\) 那么 \(i\)\(\text{S}\)。考虑一下剩下的情况,首先左右的众数不同,那么只需要判断一下不是左右的众数即可,就是不能满足 \(Q[l+1,i-1]+1=Q[l+1,i],Q[i+1,r-1]+1=Q[i,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)

using namespace std;

const int N=1005;

int id, T, n, f[N][N], l, r, tag[N];

void mian() {
	memset(tag,0,sizeof(tag));
	memset(f,0,sizeof(f));
	cin >> n, l=1, r=n;
	up(i,1,n) up(j,i,n) cin >> f[i][j];
	while(f[1][n]==f[l+1][n]) ++l;
	while(f[1][n]==f[1][r-1]) --r;
	tag[l]=tag[r]=1;
	up(i,l+1,r-1) {	
		if(f[l][i]==f[l+1][i]+1&&f[l][i]==f[l][i-1]+1) tag[i]=1;
		if(f[i][r]==f[i][r-1]+1&&f[i][r]==f[i+1][r]+1) tag[i]=1;
		if(f[l+1][i-1]==f[l][i]&&f[i+1][r-1]==f[i][r]) tag[i]=1;
	}
	up(i,1,n) if(tag[i]) cout << i << ' '; cout << '\n'; 
}

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


英雄传说hero

考虑快乐的英雄集合 \(A\),悲伤的英雄集合 \(B\),我们肯定拿 \(A\) 依次匹配 \(b\downarrow\) 的怪物,拿 \(B\) 依次匹配 \(b\uparrow\) 的怪物。这个把序列分成了前后缀(前缀怪物使高兴、后缀怪物使悲伤),那么前缀的状态可以用 \(f_{i,j}\) 即考虑到 \(i\) 还有 \(j\) 个怪物可以用来表示了(这个跟两种方向的英雄有等式关系),对前后缀 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=5005, M=10005, P=998244353;

int n, m, q, tmp, tot, tag[M], p[N], f[N][N], g[N][N], Ans[N];

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

signed main() {
	freopen("hero.in","r",stdin);
	freopen("hero.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n, m=(n<<1);
	up(i,1,n) cin >> tmp, tag[tmp]=1;
	up(i,1,n) cin >> tmp, tag[tmp]=0;
	up(i,1,m) if(tag[i]) p[++tot]=i;
	f[0][0]=g[n+1][0]=1, p[n+1]=m+1;
	up(i,1,n+1) {
		int d=p[i]-p[i-1]-1;
		up(j,0,n) if(f[i-1][j]) {
			add(f[i][j+d],f[i-1][j]);
			if(j+d-1>=0) add(f[i][j+d-1],f[i-1][j]);
		}
	}
	dn(i,n,0) {
		int d=p[i+1]-p[i]-1;
		up(j,0,n) if(g[i+1][j]) {
			add(g[i][j+d],g[i+1][j]);
			if(j+d-1>=0) add(g[i][j+d-1],g[i+1][j]);
		}
	}
	add(Ans[0],g[1][0]*tag[1]);
	up(u,0,n) {
		int l=p[u], r=p[u+1];
		up(i,l+1,r-1) up(jl,0,l) {
			int pl=jl+2*u-l, jr=pl-(r-i-1), pr=(m-r+1)-(n-u)-jr, k=pl+pr;
			if(pl<0||pl>u||jr<0||jr>m-r+1||pr<0||pr>n-u) continue;
			if(0<=k&&k<=n) add(Ans[n-k],f[u][jl]*g[u+1][jr]%P);
		}
	}
	up(i,1,n) add(Ans[i],Ans[i-1]);
	cin >> q;
	while(q--) {
		int l, r, ans;
		cin >> l >> r;
		ans=(Ans[r]-(l?Ans[l-1]:0))%P;
		cout << (ans%P+P)%P << '\n';
	}
	return 0;
}

二叉竖bst

这是好题,顺便赞扬一下操作的 \(x\) 互不相同。

想想只给 bst 加数,查询的答案 \(x\) 怎么算。某个时刻加入 \(v\),若 \(v\leq x\),那么在这之后的 \(v'\in [1,v]\) 无用,若 \(v\geq x\),那么在这之后的 \(v'\in [v,\infty)\) 无用,意思就是 \(v\leq x\)\(v\geq x\) 的部分相互独立,嗯之后只用进一步考虑 \(v\leq x\) 的做法,另一侧是一致的,按照时间顺序能加能加出一个 \(v\uparrow\) 的序列,

题目维度很多,先考虑枚举个什么,枚举 \(t\) 像疯了,枚举 \(v\) 要动态加 \((l,r,t,v)\) 和查询 \(u\),枚举 \(u\) 只需要动态加删 \((t,x)\) 查询 \((T,X)\),枚举 \(u\) 看着最有道理,之后完全可以扔掉 \(u\) 了。动态加删考虑线段树,以 \(t\) 为下标 \(x\) 为权值建立的线段树要在前缀上查询 \(<X\) 的答案,对于每个点都有好多种情况感觉维护不了,以 \(x\) 为下标 \(t\) 为权值建立线段树,要做对前缀查询 \(<T\) 的答案,首先每个点的原始贡献是确定唯一且可以合并的,合并的时候写个对 \(low\) 的查询即可,先确定右边再确定就好了,查询就类似了,写代码注意用脑子。

#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 ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define pii pair<int,int>
#define mp make_pair
#define low first
#define val second

using namespace std;

const int N=600005, M=2400005;

int n, m, xa, xb, tot, sp[N], Ans[N];
int ans[M], con[M], low[M];
struct node { int u, t, x; } a[N], b[N];

pii query(int l,int r,int t,int p=1,int s=1,int e=tot) {
	if(l>r) return mp(0,0);
	int mid=(s+e)>>1;
	if(l<=s&&e<=r) {
		if(!low[p]||low[p]>t) return mp(0,0);
		if(s==e) {
			return mp(low[p],ans[p]);
		}
		if(!low[rs(p)]||t<low[rs(p)]) {
			pii ret=query(l,r,t,ls(p),s,mid);
			return ret;
		}
		pii ret=query(l,r,t,rs(p),mid+1,e);
		return mp(low[ls(p)]?min(low[ls(p)],ret.low):ret.low,con[p]+ret.val); 
	}
	pii x=mp(0,0), y=mp(0,0);
	if(r>mid) x=query(l,r,t,rs(p),mid+1,e);
	if(l<=mid) y=query(l,r,x.low?x.low:t,ls(p),s,mid);
	return mp(y.low?y.low:x.low,x.val+y.val);
}

inline void tup(int p,int s,int e) {
	if(low[ls(p)]&&low[rs(p)]) low[p]=min(low[ls(p)],low[rs(p)]);
	else low[p]=low[ls(p)]+low[rs(p)];
	int mid=(s+e)>>1;
	if(low[rs(p)]) con[p]=query(s,e,low[rs(p)],ls(p),s,mid).val;
	else con[p]=query(s,e,m+1,ls(p),s,mid).val;
	ans[p]=con[p]+ans[rs(p)];
}

void updata(int real,int x,int t,int p=1,int s=1,int e=tot) {
	if(s==e) {
		ans[p]=real, low[p]=t, con[p]=0;
		return;
	}
	int mid=(s+e)>>1;
	if(x<=mid) updata(real,x,t,ls(p),s,mid);
	if(x>mid) updata(real,x,t,rs(p),mid+1,e);
	tup(p,s,e);
}

signed main() {
	freopen("bst.in","r",stdin);
	freopen("bst.out","w",stdout); 
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m;
	up(i,1,m) {
		int opt, l, r, x;
		cin >> opt;
		if(opt==1) {
			cin >> l >> r >> x;
			a[++xa]=(node){l,i,x};
			a[++xa]=(node){r+1,i,-x};
			sp[++tot]=x;
			Ans[i]=-1;
		}
		else {
			cin >> l >> x;
			b[++xb]=(node){l,i,x};
			sp[++tot]=x;
		}
	}
	sort(sp+1,sp+1+tot), tot=unique(sp+1,sp+1+tot)-sp-1;
	up(i,1,xa) {
		bool flag=a[i].x<0; a[i].x=abs(a[i].x);
		a[i].x=lower_bound(sp+1,sp+1+tot,a[i].x)-sp;
		if(flag) a[i].x=-a[i].x;
	}
	up(i,1,xb) b[i].x=lower_bound(sp+1,sp+1+tot,b[i].x)-sp;
	sort(a+1,a+1+xa,[](node i,node j){return i.u<j.u;});
	sort(b+1,b+1+xb,[](node i,node j){return i.u<j.u;});
	map<int,int> ins;
	int j=1;
	up(i,1,xb) {
		while(j<=xa&&a[j].u<=b[i].u) {
			if(a[j].x>0) {
				updata(sp[a[j].x],a[j].x,a[j].t);
				ins[a[j].x]=a[j].t;
			}
			else {
				updata(0,-a[j].x,0);
				ins[-a[j].x]=0;
			}
			++j;
		}
		Ans[b[i].t]=query(1,b[i].x,b[i].t).val;
		if(ins[b[i].x]&&ins[b[i].x]<b[i].t) Ans[b[i].t]-=sp[b[i].x];
	}
	memset(ans,0,sizeof(ans));
	memset(con,0,sizeof(con));
	memset(low,0,sizeof(low));
	j=1;
	up(i,1,xb) {
		while(j<=xa&&a[j].u<=b[i].u) {
			if(a[j].x>0) updata(sp[a[j].x],tot-a[j].x+1,a[j].t); else updata(0,tot+a[j].x+1,0);
			++j;
		}
		int sav=query(1,tot-b[i].x+1,b[i].t).val;
		Ans[b[i].t]+=sav;
	}
	up(i,1,m) if(Ans[i]>=0) cout << Ans[i] << '\n';
	return 0;
}
posted @ 2025-09-26 22:07  Hypoxia571  阅读(12)  评论(0)    收藏  举报