被彼此笼罩 任泪水将我们缠绕 深陷入恶魔的拥抱 在阴冷黑暗处灼烧 吞下这毒药
test5
方格染色grid
不难发现按着行顺着来,odt 那样维护即可。
#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
using namespace std;
const int N=200005;
int n, a[N], b[N], ans, col, L, R;
map<int,int> cnt;
struct node {
int l, r, t, v;
} p[N];
signed main() {
freopen("grid.in","r",stdin);
freopen("grid.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
up(i,1,n) cin >> a[i], ++cnt[a[i]];
up(i,1,n) cin >> b[i], ++cnt[b[i]];
--cnt[a[1]], b[2]=max(b[2],a[2]);
L=1, R=0, p[R].r=1;
up(i,3,n) b[i]=max(b[i-1],b[i]);
up(i,2,n) if(b[i]!=b[i+1]) ++R, p[R]=(node){p[R-1].r+1,i,2,b[i]};
up(i,3,n) {
bool flag=0;
while(L<=R&&a[i]>p[L].v) {
cnt[p[L].v]+=(p[L].r-p[L].l+1)*(i-p[L].t);
flag=1, ++L;
}
if(flag) {
int lim=(L<=R?p[L].l-1:n);
p[--L]=(node){2,lim,i,a[i]};
}
}
while(L<=R) cnt[p[R].v]+=(p[R].r-p[R].l+1)*(n-p[R].t+1), --R;
for(pii i:cnt) if(i.second>ans||i.second==ans&&i.first>col) col=i.first, ans=i.second;
cout << col << ' ' << ans << '\n';
return 0;
}
数字图graph
为什么本可做这个题做了很久(?
首先显然可以二分降低难度,然后就是观察。因为 \(2|2^{100}\) 所以平均也算某人赢了哦,发现平的不好做考虑做一下另一边能不能赢,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)
#define pb push_back
using namespace std;
const int N=500005;
int n, m, a[N], sp[N], tag[N], f[N], vis[N];
vector<int> to[N], F[N];
queue<int> q;
bool check(int low) {
if(a[1]>=low) return 1;
up(i,1,n) {
tag[i]=(a[i]<low);
f[i]=vis[i]=0, F[i].clear();
}
up(i,1,n) for(int j:to[i]) {
if(tag[i]==tag[j]) continue;
F[j].pb(i);
if(tag[j]) ++vis[i];
}
up(i,1,n) if(!tag[i]&&!vis[i]) f[i]=1, q.push(i);
while(q.size()) {
int x=q.front(); q.pop();
for(int y:F[x]) {
if(tag[x]) {
if(!--vis[y]) {
f[y]=1;
q.push(y);
}
}
else {
if(!f[y]) {
f[y]=1;
q.push(y);
}
}
}
}
return f[1];
}
signed main() {
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
up(i,1,n) {
cin >> a[i];
sp[i]=a[i];
}
while(m--) {
int u, v;
cin >> u >> v;
to[u].pb(v);
}
sort(sp+1,sp+1+n);
int l=1, r=unique(sp+1,sp+1+n)-sp-1, ans;
while(l<=r) {
int mid=(l+r)>>1;
if(check(sp[mid])) ans=mid, l=mid+1;
else r=mid-1;
}
cout << sp[ans] << '\n';
return 0;
}
救护车(car)
首先坚定这个题目基于背包。然后就是有多种枚举方式的时候优先选择更方便的。
不难观察到对于同一条边的两个端点,是按照 \(x/y=k\) 的斜线来划分的,就是一个只在某个前缀取,另一个只在某个后缀取,前后缀不相交。对于对角的两个端点,就是按照一条曲折斜线(不严格单调)来划分的,就是分别拿 \(x+y/x-y\uparrow\) 的一个前缀或者后缀。
先枚举 \(x+y\uparrow\) 的分界线,可以分别计算出上下两边在给 \((1,1)/(L,L)\) 的贡献合法的前提下给 \(x-y\uparrow\) 端点的贡献不超过 \(i\) 时给 \(x-y\downarrow\) 端点的最小贡献 \(con[i]\)。这个计算方式是对于上半/下半按照 \(x-y\downarrow\) 的顺序划分成前后缀,对前后缀考虑 \(f[i][j]\) 表示前/后缀到 \(i\),对中心的贡献为 \(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=165, M=10005;
int L, n, T, f1[N][M], f2[N][M], g1[N][M], g2[N][M], lsy[M], lkx[M];
struct node { int x, y; } a[N], p1[N], p2[N];
bool cmp1(node i,node j) { return i.x+i.y<j.x+j.y; }
bool cmp2(node i,node j) { return i.x-i.y>j.x-j.y; }
inline void chk(int &a,int b) { a=min(a,b); }
bool ranger(int x) { return 0<=x&&x<=T; }
signed main() {
// freopen("1.txt","r",stdin);
freopen("car.in","r",stdin);
freopen("car.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> L >> n >> T, T/=2;
up(i,1,n) cin >> a[i].x >> a[i].y;
sort(a+1,a+1+n,cmp1);
up(u,0,n) {
memset(f1,0x3f,sizeof(f1));
memset(f2,0x3f,sizeof(f2));
memset(g1,0x3f,sizeof(g1));
memset(g2,0x3f,sizeof(g2));
memset(lsy,0x3f,sizeof(lsy));
memset(lkx,0x3f,sizeof(lkx));
int len1=u, len2=n-u;
up(i,1,len1) p1[i]=a[i];
up(i,1,len2) p2[i]=a[len1+i];
sort(p1+1,p1+1+len1,cmp2);
sort(p2+1,p2+1+len2,cmp2);
f1[0][0]=f2[len1+1][0]=g1[0][0]=g2[len2+1][0]=0;
up(i,0,len1) up(j,0,T) {
chk(f1[i][j+1],f1[i][j]);
if(i<len1&&ranger(j+p1[i+1].x-1+p1[i+1].y-1)) chk(f1[i+1][j+p1[i+1].x-1+p1[i+1].y-1],f1[i][j]);
if(i<len1) chk(f1[i+1][j],f1[i][j]+L-p1[i+1].x+p1[i+1].y-1);
}
dn(i,len1+1,1) up(j,0,T) {
chk(f2[i][j+1],f2[i][j]);
if(i>1&&ranger(j+p1[i-1].x-1+p1[i-1].y-1)) chk(f2[i-1][j+p1[i-1].x-1+p1[i-1].y-1],f2[i][j]);
if(i>1) chk(f2[i-1][j],f2[i][j]+p1[i-1].x-1+L-p1[i-1].y);
}
up(i,0,len2) up(j,0,T) {
chk(g1[i][j+1],g1[i][j]);
if(i<len2&&ranger(j+L-p2[i+1].x+L-p2[i+1].y)) chk(g1[i+1][j+L-p2[i+1].x+L-p2[i+1].y],g1[i][j]);
if(i<len2) chk(g1[i+1][j],g1[i][j]+L-p2[i+1].x+p2[i+1].y-1);
}
dn(i,len2+1,1) up(j,0,T) {
chk(g2[i][j+1],g2[i][j]);
if(i>1&&ranger(j+L-p2[i-1].x+L-p2[i-1].y)) chk(g2[i-1][j+L-p2[i-1].x+L-p2[i-1].y],g2[i][j]);
if(i>1) chk(g2[i-1][j],g2[i][j]+p2[i-1].x-1+L-p2[i-1].y);
}
up(i,0,len1) up(j,0,T) if(f1[i][j]<=T) chk(lsy[f1[i][j]],f2[i+1][T-j]);
up(i,0,len2) up(j,0,T) if(g1[i][j]<=T) chk(lkx[g1[i][j]],g2[i+1][T-j]);
up(i,1,T) chk(lsy[i],lsy[i-1]), chk(lkx[i],lkx[i-1]);
up(j,0,T) if(lsy[j]+lkx[T-j]<=T) { cout << "Yes\n"; return 0; }
}
cout << "No\n";
return 0;
}
打卡tour
谁能教教我怎么拆贡献好看呐。走法肯定依次拓展左右左 \(\{l_{1,\dots,m_1},r_{1,\dots,m_2}\}\) 这样子,考虑左边的贡献,就是直接走的贡献加上 \(2(x_{r_i}-x_{l_i})\times pre\) 这种东西。
考虑 \(f_{l,r,0/1}\) 表示左/右边扫到 \(l/r\) 且停在 \(l/r\) 的最小答案,转移应该是 \(f_{l,r,0}=\min\{f_{p,r,1}+|x_r-x_l|\times (l-1)\},f_{l,r,1}=\min\{f_{l,p,0}+|x_r-x_l|\times(n-r)\}\),不关心 \(p\) 于是拆成 \(f/g\) 两个数组,再对 \(x\) 做一些美化处理,转移就是 \(f_l=\min \{g_r+2(x_l+x_r)(xb-r)\},g_r=\min\{f_l+2(x_l+x_r)(xa-l)\}\),乱序转移又没有负权不妨考虑 dij,发现还需要考虑一下快速查找更新的事情,贡献是直线可以考虑李超树,斜率优化也行,最后就是这个题目实现的时候注意到 \(f,g\uparrow\) 可以少些一些东西,以及比较要非严格小于不然会越界(?)
#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 k first
#define b second
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define pii pair<int,int>
#define mp make_pair
using namespace std;
const int N=300005, inf=1e13;
int n, k, d[N], x[N], a[N], b[N], Ans;
struct node {
int len, sp[N]; pii tr[N<<3];
void insert(int v) { sp[++len]=v; }
void build(pii v) { up(i,1,4*len) tr[i]=v; }
inline int calc(pii i,int x) {
if(!i.k&&!i.b) return inf;
return i.k*sp[x]+i.b;
}
inline bool pd(pii i,pii j,int x) { return i.k*sp[x]+i.b<=j.k*sp[x]+j.b; }
void updata(pii v,int p=1,int s=1,int e=0) {
if(!e) e=len;
if(pd(tr[p],v,s)&&pd(tr[p],v,e)) return;
if(pd(v,tr[p],s)&&pd(v,tr[p],e)) { tr[p]=v; return; }
int mid=(s+e)>>1;
if(pd(v,tr[p],mid)) swap(tr[p],v);
if(pd(v,tr[p],s)) updata(v,ls(p),s,mid);
else updata(v,rs(p),mid+1,e);
}
int query(int l,int x,int p=1,int s=1,int e=0) {
if(!e) e=len;
if(s==e) return calc(tr[p],x);
int mid=(s+e)>>1, res=inf;
if(l<=mid) res=query(l,x,ls(p),s,mid);
else res=query(l,x,rs(p),mid+1,e);
return min(res,calc(tr[p],x));
}
} L, R;
signed main() {
freopen("tour.in","r",stdin);
freopen("tour.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> k;
up(i,2,n) cin >> d[i], x[i]=x[i-1]+d[i];
dn(i,k-1,1) Ans+=(x[i+1]-x[i])*(i-1+1);
up(i,k+1,n) Ans+=(x[i]-x[i-1])*(n-i+1);
dn(i,k-1,1) L.insert(x[k]-x[i]);
up(i,k+1,n) R.insert(x[i]-x[k]);
if(!L.len||!R.len) { cout << Ans << '\n'; return 0; }
L.build(mp(2*R.len,Ans)), R.build(mp(2*L.len,Ans));
for(int i=1, j=1; ; ) {
int f=L.query(i,i), g=R.query(j,j);
if(f<g) {
if(i==L.len) { cout << f << '\n'; return 0; }
f+=2*(L.len-i)*L.sp[i], R.updata(mp(2*(L.len-i),f)), ++i;
}
else {
if(j==R.len) { cout << g << '\n'; return 0; }
g+=2*(R.len-j)*R.sp[j], L.updata(mp(2*(R.len-j),g)), ++j;
}
}
return 0;
}