骄傲 雨伞边缘处的暗槽 从最原初裂缝开凿 被碰触和温暖击倒 停止思考

test15

公交bus

题目都帮忙写好了 \(i\) 合法等价于存在车 \(j\) 满足 \(|x_j-x_i|\leq t_j-t_i\),也就是同时满足 \(t_i-x_i\leq t_j-x_j\)\(x_i+t_i\leq x_j+t_j\),是一个二维偏序的形式,答案显然就是不能被偏序的点的数量,按一维排序后单调栈求解即可。

#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 T, n, x, t, st[N], top;
struct node {
	int a, b;
	bool operator<(const node &rhs) const {
		if(b!=rhs.b) return b<rhs.b;
		return a<rhs.a;
	}
} p[N];

void mian() {
	cin >> n;
	up(i,1,n) {
		cin >> x >> t;
		p[i]=(node){t-x,x+t};
	}
	sort(p+1,p+1+n);
	top=0;
	up(i,1,n) {
		while(top&&p[st[top]].a<=p[i].a) --top;
		st[++top]=i;
	} 
	cout << top << '\n';
}

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

图论graph

如果 \(E\) 的答案是 \(\sum_{i\in E}w_i-\max_{i\in E}{w_i}\) 的话显然只需要记录 \(dis_{0/1}[i]\) 表示没有/有少算一条边的贡献的最短路,正确性包含在最短路里面,换到此题只要按照选取方式分别维护最短路即可。

#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=200005, inf=1e18;

int n, m, dis[N][4], tag[N][4];
vector<pii> to[N];
struct node {
	int x, opt, val;
	node(int X,int Opt,int Val) { x=X, opt=Opt, val=Val; }
	bool operator<(const node &rhs) const { return val>rhs.val; }
}; priority_queue<node> q;

signed main() {
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m;
	while(m--) {
		int u, v, w;
		cin >> u >> v >> w;
		to[u].pb(mp(v,w));
		to[v].pb(mp(u,w));
	}
	up(i,1,n) dis[i][0]=dis[i][1]=dis[i][2]=dis[i][3]=inf;
	dis[1][0]=0, q.push(node(1,0,0));
	while(q.size()) {
		int x=q.top().x, o=q.top().opt;
		q.pop();
		if(tag[x][o]) continue;
		tag[x][o]=1;
		for(pii p:to[x]) {
			int y=p.first, w=p.second;
			if(dis[x][o]+w<dis[y][o]) {
				dis[y][o]=dis[x][o]+w;
				q.push(node(y,o,dis[y][o]));
			}
			if(o==0) {
				if(dis[x][o]+w+w<dis[y][1]) {
					dis[y][1]=dis[x][o]+w+w;
					q.push(node(y,1,dis[y][1]));
				}
				if(dis[x][o]<dis[y][2]) {
					dis[y][2]=dis[x][o];
					q.push(node(y,2,dis[y][2]));
				}
			}
			if(o==1) {
				if(dis[x][o]<dis[y][3]) {
					dis[y][3]=dis[x][o];
					q.push(node(y,3,dis[y][3]));
				}
			}
			if(o==2) {
				if(dis[x][o]+w+w<dis[y][3]) {
					dis[y][3]=dis[x][o]+w+w;
					q.push(node(y,3,dis[y][3]));
				}
			}
		}
	}
	up(i,2,n) {
		int ans=min(dis[i][0],dis[i][3]);
		if(ans>=inf) ans=-1;
		cout << ans << ' ';
	}
	return 0;
}

打怪升级monster

首先如果一个城市被多次经过,那么一定是最后一次计算贡献。不妨设算贡献的城市顺序为 \(u_1,\dots,u_m\),首先关心的 \(dis(u_{i},u_{i+1})\) 可以预处理。现在考虑时间为 \(T\) 最优答案是多少,首先显然尽可能在起点等待,然后直接顺次走完是最优秀的,总贡献就是 \(\sum_{i=1}^m (\sum_{j=i}^{m-1}dis(u_j,u_{j+1}))\times a_{u_i}\),更好 dp 的形式是考虑新加入的 \(u_{m'}=i\) 的贡献 \(\sum_{j=1}^{m'-1}a_{u_j}\times dis(u_{m'-1},i)\),又因为关心终点,设 \(f[s][i]\) 表示选择集合 \(s\) 的点、终点为 \(i\)、最少减多少贡献,状压求出来就行惹。然后就是有点怕 \(\sum_{i=1}^{m-1} dis(u_i,u_{i+1})>T\) 怎么办,但是不难发现这个不优。

最后问题变成了有若干直线求某个横坐标下的最大值是多少,可以斜率优化,先对 \(k\) 去重(要 \(b\) 最大的),然后 \(k\uparrow\) 排序,计算每根线在哪里最优,询问直接在上面二分就好惹 qwq。

#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=19, inf=2e18;

int n, m, q, a[N], dis[N][N], f[1<<N][N];
struct node {
	int tot, fc[1<<N];
	pii l[1<<N], p[1<<N];
	void insert(int k,int b) {
		l[++tot]=mp(k,b);
	}
	void Sort() {
		sort(l+1,l+1+tot);
		int ran=0;
		up(i,1,tot) {
			if(i>1&&l[i].first==l[ran].first) l[ran]=l[i];
			else l[++ran]=l[i];
		} tot=ran, ran=0;
		up(i,1,tot) {
			while(ran>0) {
				int t=(p[ran].second-l[i].second)/(l[i].first-p[ran].first);
				if((p[ran].second-l[i].second)%(l[i].first-p[ran].first)) ++t;
				if(t<=fc[ran]) --ran; else break;
			}
			int t=0;
			if(ran) {
				t=(p[ran].second-l[i].second)/(l[i].first-p[ran].first);
				if((p[ran].second-l[i].second)%(l[i].first-p[ran].first)) ++t;
			}
			p[++ran]=l[i], fc[ran]=t;
		} tot=ran;
	}
	int query(int T) {
		int i=upper_bound(fc+1,fc+1+tot,T)-fc-1;
		return p[i].first*T+p[i].second;
	} 
} qwq[N];

/*
3300028
13143616
*/ 
signed main() {
	freopen("monster.in","r",stdin);
	freopen("monster.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> m;
	up(i,1,n) cin >> a[i];
	up(i,1,n) up(j,1,n) dis[i][j]=inf;
	up(i,1,n) dis[i][i]=0;
	while(m--) {
		int u, v, w;
		cin >> u >> v >> w;
		dis[u][v]=min(dis[u][v],w);
	}
	up(k,1,n) up(i,1,n) up(j,1,n) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
	memset(f,0x3f,sizeof(f));
	up(s,1,(1<<n)-1) up(i,1,n) f[s][i]=inf;
	up(i,1,n) f[1<<i-1][i]=0;
	up(s,1,(1<<n)-1) {
		int sum=0;
		up(i,1,n) if(s>>(i-1)&1) sum+=a[i];
		up(i,1,n) if(!(s>>(i-1)&1)) {
			int ss=s^(1<<i-1);
			up(pre,1,n) if(s>>(pre-1)&1) {
				if(f[s][pre]>=inf||dis[pre][i]>=inf) continue;
				f[ss][i]=min(f[ss][i],f[s][pre]+sum*dis[pre][i]);
			} 
		}
	}
	up(u,1,n) {
		up(s,1,(1<<n)-1) {
			if(!(s>>(u-1)&1)) continue;
			int sum=0;
			up(i,1,n) if(s>>(i-1)&1) sum+=a[i];
			// f(t) = sum * t - f[s]
			qwq[u].insert(sum,-f[s][u]);
		}
		qwq[u].Sort();
	}
	cin >> q;
	while(q--) {
		int T, p;
		cin >> T >> p;
		cout << qwq[p].query(T) << '\n';
	}
	return 0;
}

世界树tree

怎么 t4 真的放贪心。

首先原序列的合法条件是 \(\forall i ,\sum_{j=1}^{2n-2}[a_j<i]\geq i-1\),不妨令 \(s_i=\sum_{j=1}^{2n-2}[a_j<i]-(i-1)\),合法条件就是 \(\forall s_i\geq 0\)

现在考虑选择一组 \(u_1,\dots,u_m(u_1=1,u_m<n)\) 的影响,显然是令 \(s_{u_{i-1}+1,\dots,u_i-1}\) 减小 \(1\),所以首先 \(s_i=0\) 的必须要选择。后面就是 \(cnt_i>0,s_i>0\) 的地方随便加不加惹,那么贪心从小到大加,然后答案就是从大到小计算贡献,用堆维护即可。

#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 n, m, a[N], cnt[N], s[N], tag[N], Ans[N], ans, c1[N], c2[N];
priority_queue<int> qwq;

int top() {
	while(qwq.size()) {
		int x=qwq.top(); qwq.pop();
		if(!c1[x]) continue;
		--c1[x], ++c2[x]; return x;
	}
}

signed main() {
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
//	freopen("1.txt","r",stdin);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n;
	up(i,1,2*(n-1)) cin >> a[i], ++cnt[a[i]];
	int sum=0;
	up(i,1,n) {
		s[i]=sum-(i-1), sum+=cnt[i];
		if(s[i]==0) {
			if(!cnt[i]) {
				up(i,1,n-1) cout << -1 << ' ';
				return 0;
			}
			tag[i]=1, ++m;
		}
	}
//	cout << "s : "; up(i,1,n) cout << s[i] << ' '; cout << '\n';
//	cout << "m : "; up(i,1,n) if(tag[i]) cout << i << ' '; cout << '\n'; 
	up(i,1,n) {
		up(j,2,cnt[i]) ++c1[i], qwq.push(i);
		if(cnt[i]&&!tag[i]) ++c1[i], qwq.push(i);
	}
	up(i,1,m) ans+=top();
	Ans[m]=ans;
//	cout << "pre : "; for(int i:qwq) cout << i << ' '; cout << '\n';
//	cout << m << ' ' << ans << '\n'; 
	up(i,2,n-1) {
		if(tag[i]||!cnt[i]) continue;
		if(c1[i]) --c1[i];
		else --c2[i], ans-=i, ans+=top();
		ans+=top(), Ans[++m]=ans;
	}
	up(i,1,n-1) {
		if(!Ans[i]) Ans[i]=-1;
		cout << Ans[i] << ' ';
	}
	return 0;
}
posted @ 2025-10-09 22:12  Hypoxia571  阅读(6)  评论(0)    收藏  举报