骄傲 雨伞边缘处的暗槽 从最原初裂缝开凿 被碰触和温暖击倒 停止思考
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;
}