齿轮运行思考的海马体 渐渐懂得黑与白不同意义 这就是我缺少的吗
test19
深度优先搜索dfs
先不考虑顺序,一次操作相当于 \((x,x+1)\to x\),那么显然是从大到小一直一直做到不行,要求只有 \(1\) 个 \(0\) 且对于 \(a_i\) 存在 \(a_i-1\)。
那么我们在有序数列上考虑类似的过程,拿取一个最大的 \(a\) 删掉(有多个优先拿最边缘的,不难发现不相互影响),相当于要求其左右第一个更小的数至少有一个是 \(a_{i-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=100005;
int T, n, a[N], L[N], R[N], stk[N], top;
void mian() {
cin >> n;
up(i,1,n) cin >> a[i];
stk[top=0]=-1;
up(i,1,n) {
while(top&&stk[top]>=a[i]) --top;
L[i]=stk[top], stk[++top]=a[i];
}
stk[top=0]=-1;
dn(i,n,1) {
while(top&&stk[top]>=a[i]) --top;
R[i]=stk[top], stk[++top]=a[i];
}
int cnt=0;
up(i,1,n) {
if(a[i]==0) ++cnt;
if(a[i]&&(L[i]==-1||L[i]+1!=a[i])&&(R[i]==-1||R[i]+1!=a[i])) {
cout << "NO\n";
return;
}
}
if(cnt==1) cout << "YES\n";
else cout << "NO\n";
}
signed main() {
freopen("dfs.in","r",stdin);
freopen("dfs.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
while(T--) mian();
return 0;
}
弹珠pinball
好像为数不多打的几次 cf 里面就包含了这道题 /fad
手动模拟这个过程画图看看,设 \(pre_i/suf_i\) 表示前/缀的 \(\text{>,<}\) 个数,最终走哪边是可以用 \(str_i,pre_{i-1},suf_{i+1}\) 确定的贡献一个 \(i/n-i+1\),别的贡献拆成很多个走到一个逆向点再走回起点的过程,计算左右最后 \(\min(pre_{i-1},suf_{i+1})(+1)\) 个 \(\text{>,<}\) 的位置和即可,懒得说的很详细惹。
#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=200005;
int T, n, pre[N], suf[N], L[N], R[N], Ans[N], tot, sum[N];
char str[N];
void mian() {
cin >> n >> (str+1), suf[n+1]=0;
up(i,1,n) pre[i]=pre[i-1]+(str[i]=='>');
dn(i,n,1) suf[i]=suf[i+1]+(str[i]=='<');
up(i,1,n) {
L[i]=R[i]=min(pre[i-1],suf[i+1]);
if(pre[i-1]>suf[i+1]&&str[i]=='<') ++L[i];
if(pre[i-1]<suf[i+1]&&str[i]=='>') ++R[i];
if(pre[i-1]<suf[i+1]||pre[i-1]==suf[i+1]&&str[i]=='<') Ans[i]=i;
if(pre[i-1]>suf[i+1]||pre[i-1]==suf[i+1]&&str[i]=='>') Ans[i]=n-i+1;
}
sum[tot=0]=0;
up(i,1,n) {
Ans[i]+=2*(L[i]*i-sum[tot]+sum[tot-L[i]]);
if(str[i]=='>') ++tot, sum[tot]=sum[tot-1]+i;
}
sum[tot=0]=0;
dn(i,n,1) {
Ans[i]+=2*(sum[tot]-sum[tot-R[i]]-R[i]*i);
if(str[i]=='<') ++tot, sum[tot]=sum[tot-1]+i;
}
up(i,1,n) cout << Ans[i] << ' '; cout << '\n';
}
signed main() {
freopen("pinball.in","r",stdin);
freopen("pinball.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
while(T--) mian();
return 0;
}
字符串string
设 \(fc_{i}=1/-1/0[a_i<a_{i-1}/a_i>a_{i-1}/a_i=a_{i-1}]\),那么原题目的限制 \(a<b\) 就是要求 \(fc_{a+1,\dots,b}=00001\),\(a=b\) 不用管,\(a>b\) 就是要求 \(fc_{b+1,\dots,a}=0000-1\)。意思就是对于选择有两维度限制,能不能选更大/更小的数,而满足条件的前提下变化一个值会清空限制。
带着 \(fc_i=0\) 和限制不太好 dp,不妨考虑 \(f_{i,j}\) 表示最后一个 \(fc\neq 0\) 的是 \(i\) 的答案,转移 \(i\) 的时候对于 \(j<i\) 只关心存不存在跨越的每种限制,每一类型的 \(j\) 显然是一个区间,前缀和优化即可。
#define _CRT_SECURE_NO_WARNINGS
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#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=100005, P=1e9+7, lim=26;
int id, T, n, m, Ans, f[N][27], L[N], R[N], p[N][28], q[N][28];
vector<int> add1[N], del1[N], add2[N], del2[N];
inline void add(int &a,int b) { a=(a+b)%P; }
void mian() {
memset(f,0,sizeof(f));
memset(p,0,sizeof(p));
memset(q,0,sizeof(q));
cin >> n >> m, Ans=0;
while(m--) {
int a, b;
cin >> a >> b;
if(a<b) add1[a+1].pb(a+1), del1[b].pb(a+1);
if(a>b) add2[b+1].pb(b+1), del2[a].pb(b+1);
}
multiset<int> qwq;
up(i,1,n) {
for(int v:add1[i]) qwq.insert(v);
if(qwq.size()) L[i]=*--qwq.end()-1; else L[i]=0;
for(int v:del1[i]) qwq.erase(qwq.find(v));
add1[i].clear(), del1[i].clear();
} qwq.clear();
up(i,1,n) {
for(int v:add2[i]) qwq.insert(v);
if(qwq.size()) R[i]=*--qwq.end()-1; else R[i]=0;
for(int v:del2[i]) qwq.erase(qwq.find(v));
add2[i].clear(), del2[i].clear();
}
up(i,1,lim) f[1][i]=1;
up(i,1,lim) p[1][i]=(p[1][i-1]+f[1][i])%P;
dn(i,lim,1) q[1][i]=(q[1][i+1]+f[1][i])%P;
up(i,2,n) {
up(j,1,lim) add(f[i][j],f[i-1][j]);
if(L[i]&&R[i]) {
int l=min(L[i],R[i]), r=max(L[i],R[i]);
up(j,1,lim) {
if(r+1<i) {
add(f[i][j],(p[i-1][j-1]-p[r][j-1])%P);
add(f[i][j],(q[i-1][j+1]-q[r][j+1])%P);
}
if(L[i]>R[i]) {
if(l<r) add(f[i][j],(q[r][j+1]-q[l][j+1])%P);
}
else {
// if(l<r) add(f[i][j],(p[r][j-1]-p[l][j-1])%P);
if(l<r) //add(f[i][j],q[r][j+1]-q[l][j+1]);
up(v,1,j-1) {
add(f[i][j],(f[r][v]-f[l][v])%P);
}
}
}
}
else if(L[i]) {
up(j,1,lim) {
if(L[i]+1<i) {
add(f[i][j],(p[i-1][j-1]-p[L[i]][j-1])%P);
add(f[i][j],(q[i-1][j+1]-q[L[i]][j+1])%P);
}
add(f[i][j],q[L[i]][j+1]);
}
}
else if(R[i]) {
up(j,1,lim) {
if(R[i]+1<i) {
add(f[i][j],(p[i-1][j-1]-p[R[i]][j-1])%P);
add(f[i][j],(q[i-1][j+1]-q[R[i]][j+1])%P);
}
add(f[i][j],p[R[i]][j-1]);
}
}
else {
up(j,1,lim) {
add(f[i][j],p[i-1][j-1]);
add(f[i][j],q[i-1][j+1]);
}
}
up(j,1,lim) p[i][j]=(p[i][j-1]+f[i][j])%P;
dn(j,lim,1) q[i][j]=(q[i][j+1]+f[i][j])%P;
}
up(i,1,lim) add(Ans,f[n][i]);
cout << (Ans%P+P)%P << '\n';
}
signed main() {
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> id >> T;
while(T--) mian();
return 0;
}
酒厂wine
直接做好像不太好做,但是发现以初始输入水流为横坐标维护答案(输出水量、酿酒量)曲线是容易的,因为答案曲线形态很简单。对于酿酒量,最开始应该是加一点水就多一点酒,最后应该是怎么加水都不加酒,而对于输入水量,应该是最开始不变(因为拿去酿酒了),然后多一点输入水就多输出一点水,最后怎么加水都不会多输出,发现需要维护初始酿酒量 \(ans\),可额外酿酒量 \(lim\),初始出水量 \(res\),开始停出水量 \(top\),线段树做修改及即可,合并和初始化用以下等式:
\(ans=ans_l+酒_r(res_l),res=水_r(res_l),res+top-lim=水_r(res_l+top_l-lim_l),ans+lim=酒_r(res_l+top_l-lim_l)+酒_l(\infty)\)
\(ans=min(a,b),lim=b-min(a,b),res=min(c,a-ans),top=lim+c-res\)
#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
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
using namespace std;
const int N=200005;
int id, n, q, a[N], b[N], c[N];
struct node {
int ans, lim, res, top;
node() { ans=lim=res=top=0; }
node(int a,int b,int c) {
ans=min(a,b);
lim=b-ans;
res=min(c,a-ans);
top=lim+c-res;
}
int jiu(int x) {
if(x<=lim) return ans+x;
return ans+lim;
}
int shui(int x) {
if(x<=lim) return res;
if(lim<x&&x<=top) return res+x-lim;
return res+top-lim;
}
} tr[N<<2];
node merge(node l,node r) {
node ret;
ret.ans=l.ans+r.jiu(l.res);
ret.res=r.shui(l.res);
ret.lim=l.jiu(1e18)+r.jiu(l.res+l.top-l.lim)-ret.ans;
ret.top=r.shui(l.res+l.top-l.lim)+ret.lim-ret.res;
return ret;
}
void build(int p=1,int s=1,int e=n) {
if(s==e) {
tr[p]=node(a[s],b[s],c[s]);
// cout << "check " << s << ' ' << e << " : " << tr[p].ans << ' ' << tr[p].lim << ' '
// << tr[p].res << ' ' << tr[p].top << '\n';
return;
}
int mid=(s+e)>>1;
build(ls(p),s,mid), build(rs(p),mid+1,e);
tr[p]=merge(tr[ls(p)],tr[rs(p)]);
// cout << "check " << s << ' ' << e << " : " << tr[p].ans << ' ' << tr[p].lim << ' '
// << tr[p].res << ' ' << tr[p].top << '\n';
}
void updata(int x,int p=1,int s=1,int e=n) {
if(s==e) {
tr[p]=node(a[s],b[s],c[s]);
return;
}
int mid=(s+e)>>1;
if(x<=mid) updata(x,ls(p),s,mid);
if(x>mid) updata(x,rs(p),mid+1,e);
tr[p]=merge(tr[ls(p)],tr[rs(p)]);
}
signed main() {
freopen("wine.in","r",stdin);
freopen("wine.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> id >> n >> q;
up(i,1,n) cin >> a[i];
up(i,1,n) cin >> b[i];
up(i,1,n) cin >> c[i];
c[n]=0, build();
// cout << tr[1].ans << '\n';
while(q--) {
int x; cin >> x;
cin >> a[x] >> b[x] >> c[x];
updata(x), cout << tr[1].ans << '\n';
}
return 0;
}