朝圣显像 不及那人将门扉轻轻叩响 欢迎来到我的城市 嗅玫瑰绽放
test13
魔法 magic
首先 \(g\) 可以只通过 \(s_i=s_{i+1}=1\) 的数量和 \(s_1/s_{n+m}\) 的取值确定。
不管首尾只考虑第一个条件,容易得到一个做法,设第 \(i\) 个 \(1\) 在 \(l_i/r_i\),那么第 \(i\) 个 \(1\) 在 \(s3_{\min\{l_i,r_i\}\to\max\{l_i,r_i\}}\),考虑能不能贴到 \(s_3\) 中的第 \(i-1\) 个 \(1\),能就贴,不能贴就放到能放的最右边。不难发现因为串的性质不会因为贪心导致后续不合法、且贪心可以求出最优解法。
第二个限制只要强制 \(l_1,r_1\) 的选取在 \(s_3\) 的首位即可,右边贪心自然最优。
#include<bits/stdc++.h>
#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=600005;
int n, m, cnt, len, L[N], R[N], s[N], t[N], l[N], r[N], lst, ans[N], Ans, Pluto;
char str[N];
inline void read(int arr[]) {
cin >> (str+1);
int sl=strlen(str+1);
if(sl==n+m-1) {
cout << 14 << '\n';
exit(0);
} // lgxq 要求的
up(i,1,len) arr[i]=(str[i]-'0');
}
void solve() {
// cout << "s "; up(i,1,m) cout << l[i] << ' '; cout << '\n';
// cout << "t "; up(i,1,m) cout << r[i] << ' '; cout << '\n';
Ans=0; up(i,1,len) ans[i]=0;
lst=max(l[1],r[1]), ans[lst]=1;
up(i,2,m) {
if(lst+1>=min(l[i],r[i])) ans[++lst]=1;
else ans[lst=max(l[i],r[i])]=1;
}
up(i,2,len) Ans+=(ans[i-1]==ans[i]);
// cout << "w " << Ans << " : "; up(i,1,len) cout << ans[i]; cout << '\n';
Pluto=max(Pluto,Ans);
}
inline void copy() { up(i,1,m) l[i]=L[i], r[i]=R[i]; }
inline void su1() { l[1]=1; } // r[1]==1
inline void sd1() { r[1]=1; } // l[1]==1
inline void tu1() { l[m]=len; } // r[len]==1
inline void td1() { r[m]=len; } // l[len]==1
inline void su2() { up(i,1,m) { if(l[i]!=l[i-1]) break; ++l[i]; } } // r[1]==0
inline void sd2() { up(i,1,m) { if(r[i]!=r[i-1]) break; ++r[i]; } } // l[1]==0
inline void tu2() { dn(i,m,1) { if(l[i]!=l[i+1]) break; --l[i]; } } // r[len]==0
inline void td2() { dn(i,m,1) { if(r[i]!=r[i+1]) break; --r[i]; } } // l[len]==0
signed main() {
// freopen("data.txt","r",stdin);
// freopen("magic3.in","r",stdin);
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m, len=n+m, read(s), read(t);
up(i,1,len) if(s[i]) L[++cnt]=i;
dn(i,len,1) if(t[i]) R[cnt--]=i;
if(s[1]==1&&s[len]==1) copy(), sd1(), td1(), solve();
if(s[1]==1&&s[len]==0) copy(), sd1(), td2(), solve();
if(s[1]==1&&t[len]==1) copy(), sd1(), tu1(), solve();
if(s[1]==1&&t[len]==0) copy(), sd1(), tu2(), solve();
if(s[1]==0&&s[len]==1) copy(), sd2(), td1(), solve();
if(s[1]==0&&s[len]==0) copy(), sd2(), td2(), solve();
if(s[1]==0&&t[len]==1) copy(), sd2(), tu1(), solve();
if(s[1]==0&&t[len]==0) copy(), sd2(), tu2(), solve();
if(t[1]==1&&s[len]==1) copy(), su1(), td1(), solve();
if(t[1]==1&&s[len]==0) copy(), su1(), td2(), solve();
if(t[1]==1&&t[len]==1) copy(), su1(), tu1(), solve();
if(t[1]==1&&t[len]==0) copy(), su1(), tu2(), solve();
if(t[1]==0&&s[len]==1) copy(), su2(), td1(), solve();
if(t[1]==0&&s[len]==0) copy(), su2(), td2(), solve();
if(t[1]==0&&t[len]==1) copy(), su2(), tu1(), solve();
if(t[1]==0&&t[len]==0) copy(), su2(), tu2(), solve();
cout << Pluto << '\n';
return 0;
}
游戏game
暴力就是按照题意每次取可以取的最大值,问题是暴力带 \(\log\),但是数据范围看着想要我们对于每一个询问线性求解。
那么不能用堆,因为不考虑一放进就取的数那么取的数不增,不妨预处理排序,指针处理现在非即放即取的数取到 \(v\),双指针一下就好了。新加入的显然 \(>v\) 直接取不然塞到桶里面。
#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 n, m, T, p, a[N], sp[N], cnt[N], v[N], tot, j, Ans;
signed main() {
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> T;
up(i,1,n) cin >> a[i], sp[i]=a[i];
sort(sp+1,sp+1+n), m=unique(sp+1,sp+1+n)-sp-1;
up(i,1,n) a[i]=lower_bound(sp+1,sp+1+m,a[i])-sp;
while(T--) {
cin >> p, tot=Ans=0, j=m;
up(i,1,p-1) ++cnt[a[i]];
up(i,p,n) {
if(a[i]>j) v[++tot]=sp[a[i]];
else {
++cnt[a[i]];
while(j&&!cnt[j]) --j;
--cnt[j], v[++tot]=sp[j];
}
}
while(j>0) {
while(cnt[j]) --cnt[j], v[++tot]=sp[j];
--j;
}
for(int i=1; i<=n; i+=2) Ans+=v[i];
cout << Ans << '\n';
}
return 0;
}
最短路path
我感觉这题我需要深刻地进行自我反思,而做法是好表述的没有赘述的必要,问题在于我的状态设计为什么因为看错题题目(?)而固化。
思考一下怎么描述一个考虑 \(1,\dots,i\) 的状态,首先 \(dis_i\) 是必要的,然后想要转移到别的颜色需要知道前一个不同归属点的 \(dis\),需要设计 \(f_{i,l,r}\) 表示考虑 \(1,\dots,i\) 到最后一个 \(A/B\) 的最短路为 \(l/r\) 的方案数,是好转移的。
手搓一下可以发现连续段没什么用其实,设 \(s_{l,\dots,r}\) 归属相同那么最优解法肯定可以只经过 \(s_l,s_r\),进而可以发现上述的 \(l/r\) 其实应该满足 \(|l-r|\leq 2\)。实际操作 dp 中可能会出现的 \(ABBB...\) 这种可能导致 \(r-l>2\) 的显然可以令 \(r=l+2\),不影响最终的 \(\min\{l,r\}+1\)。
#include<bits/stdc++.h>
#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=4005, D=3, P=1e9+7;
int n, m, f[N][N][5][2], Ans, fa, fb;
char s[N];
inline void add(int &a,int b) { a=(a+b)%P; }
signed main() {
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m >> (s+1);
if(s[1]=='?'||s[1]=='A') f[1][1][D-1][0]=1;
if(s[1]=='?'||s[1]=='B') f[1][0][D+1][1]=1;
up(i,1,n-1) {
up(j,0,m+1) up(dk,-2,2) {
int k=j+dk;
if(k<0||k>m+1) continue;
if(s[i+1]=='?'||s[i+1]=='A') {
add(f[i+1][min(j+1,k+2)][D+k-min(j+1,k+2)][0],f[i][j][D+dk][0]);
add(f[i+1][min(j,k)+1][D+min(j+2,k)-min(j,k)-1][0],f[i][j][D+dk][1]);
}
if(s[i+1]=='?'||s[i+1]=='B') {
add(f[i+1][j][D+min(j+2,k+1)-j][1],f[i][j][D+dk][1]);
add(f[i+1][min(j,k+2)][D+min(j,k)+1-min(j,k+2)][1],f[i][j][D+dk][0]);
}
}
}
up(j,0,m+1) up(dk,-2,2) {
int k=j+dk;
if(k<0||k>m+1) continue;
if(min(j,k)+1<=m) add(Ans,(f[n][j][D+dk][0]+f[n][j][D+dk][1])%P);
}
// up(i,1,n) up(j,0,m+1) up(dk,-2,2) {
// int k=j+dk;
// if(k<0||k>m+1) continue;
// if(f[i][j][D+dk][0]) cout << "f " << i << ' ' << j << ' ' << k << ' ' << 0 << " = " << f[i][j][D+dk][0] << '\n';
// if(f[i][j][D+dk][1]) cout << "f " << i << ' ' << j << ' ' << k << ' ' << 1 << " = " << f[i][j][D+dk][1] << '\n';
// }
cout << (Ans%P+P)%P << '\n';
return 0;
}
梦魇nightmare
原题 P9058/P9678,感觉前置题目 P5926 比这个难是可以说的吗,anyway 先讲讲前置题目做法。
有贡献(即不被三维偏序)的点对应该是不多的,我们考虑什么样的点对 \((i,q)\) 是有贡献的,设 \((i,p(<q))\) 有贡献、\(a_i\leq a_q\),首先 \(a_p\geq a_q\) 否则 \((i,p)\) 偏序 \((i,q)\)。之后要满足 \(zz\) 画一下数轴就是要求 \(a_q<\frac{a_i+a_p}{2}\),那么一定在数轴上不断至少减半(\(a_{p/q}-a_i\)),所以可能有贡献的点对级别不超过 \(O(n\log n)\)。
现在考虑一下怎么求这个东西,对于 \((i,p)\) 找 \(\min q\),要求 \(q>p,a_i\leq a_q<\frac{a_i+a_p}{2}\),不妨离散化以后面的限制为下标建立线段树,前面的要求是递增的排序加入,写一个线段树二分就能算出答案。询问是查询 \(L\leq l,r\leq R\) 的 \(\min v\),二维偏序即可。
回到这个题目,想要先从树上扣下来不妨考虑点分治,然后多加了两次 \(root\to lca\) 显然不影响答案。考虑 \((i,j)\) 有贡献的条件显然是 \(a_i,a_j\) 严格是 \([i,j]\) 的最小和次小值,对于 \(i\) 找右边第一个更大/更小的即可,必须使用单调栈维护因为原题卡常,总共有 \(O(n\log n)\) 个点对。查询跟前置题目一摸一样(
#include<bits/stdc++.h>
#define ll 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 ls(p) (p<<1)
#define rs(p) (p<<1|1)
using namespace std;
inline int read() {
int X=0; bool flag=1; char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') flag=0; ch=getchar(); }
while(ch>='0'&&ch<='9') { X=(X<<1)+(X<<3)+ch-'0'; ch=getchar(); }
if(flag) return X;
return ~(X-1);
}
const int N=200005, M=7000005, Q=1000005;
const ll inf=1e18;
int n, m, T, len, siz[N], val[N], tag[N], st[N], top;
int hd[N], nxt[N<<1], eg[N<<1], to[N<<1], ovo, dis[N];
ll f[N], Ans[Q], bit[N];
struct node {
int l, r; ll v;
node() {}
node(ll L,ll R,ll V) { l=L, r=R, v=V; }
bool operator<(const node &rhs) const { return r<rhs.r; }
} p[M], q[Q];
void add(int x,ll v) {
x=n-x+1;
for( ; x<=n; x+=x&-x) bit[x]=min(bit[x],v);
}
ll ask(int x) {
x=n-x+1;
ll res=inf;
for( ; x; x-=x&-x) res=min(res,bit[x]);
return res;
}
inline void eadd(int u,int v,int w) {
nxt[++ovo]=hd[u], hd[u]=ovo, eg[ovo]=w, to[ovo]=v;
}
void dp(int x,int fad=0) {
siz[x]=1, val[x]=0;
int res=0;
for(int i=hd[x]; i; i=nxt[i]) {
int y=to[i];
if(tag[y]||y==fad) continue;
dp(y,x), siz[x]+=siz[y], val[x]=max(val[x],siz[y]);
}
dis[++len]=x;
}
void DP(int x,int fad=0) {
for(int i=hd[x]; i; i=nxt[i]) {
int y=to[i], w=eg[i];
if(tag[y]||y==fad) continue;
f[y]=f[x]+w, DP(y,x);
}
}
void div(int x) {
len=0, dp(x);
up(u,1,len) {
int i=dis[u];
val[i]=max(val[i],len-siz[i]);
if(val[i]<val[x]) x=i;
}
f[x]=0, DP(x);
sort(dis+1,dis+1+len);
// up(i,1,len) cout << dis[i] << ' ' << f[dis[i]] << '\n';
top=0;
dn(u,len,1) {
int i=dis[u];
while(top&&f[i]<f[st[top]]) --top;
if(top) {
int j=st[top];
p[++m]=node(i,j,f[i]+f[j]);
}
st[++top]=i;
}
top=0;
up(u,1,len) {
int i=dis[u];
while(top&&f[i]<f[st[top]]) --top;
if(top) {
int j=st[top];
p[++m]=node(j,i,f[i]+f[j]);
}
st[++top]=i;
}
tag[x]=1;
for(int i=hd[x]; i; i=nxt[i]) {
int y=to[i];
if(!tag[y]) div(y);
}
}
signed main() {
// freopen("1.txt","r",stdin)
freopen("nightmare.in","r",stdin);
freopen("nightmare.out","w",stdout);
n=read();
up(i,2,n) {
int u=read(), v=read(), w=read();
eadd(u,v,w), eadd(v,u,w);
}
div(1);
T=read();
up(i,1,T) {
int l=read(), r=read();
q[i]=node(l,r,i);
}
sort(p+1,p+1+m);
sort(q+1,q+1+T);
up(i,0,n+1) bit[i]=inf;
int j=1;
up(u,1,T) {
int l=q[u].l, r=q[u].r;
while(j<=m&&p[j].r<=r) add(p[j].l,p[j].v), ++j;
Ans[q[u].v]=ask(l);
}
up(i,1,T) printf("%lld\n", (Ans[i]>=inf)?-1:Ans[i]);
return 0;
}