像人偶 执着于东拼西凑 用尽了所有计谋 封不住一个缺口 不肯走
test7
病毒 virus
考虑 \((day,dis)\) 二元组,前者表示天数后者表示据上一个起点的距离,不难发现按这个排序最小的肯定是不劣的因为 \((day,dis)\) 可以成为 \((day+1,0)\)。直接对这个做最短路即可,需要额外对 \(w_{i\to j}\) 查找 \(>T\) 的时间里面最早的可以离开的时间,st 标推下去即可。
#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=300005, inf=1e13, M=22;
int n, m, t, k, d, lim[N], day[N], pre[N], f[N][M];
vector<pii> to[N];
queue<int> q;
int query(int l,int w) {
dn(i,t,0) if(l+(1<<i)-1<=d-1&&f[l][i]<w) l+=(1<<i);
return l;
}
signed main() {
freopen("virus.in","r",stdin);
freopen("virus.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
cin >> k;
cin >> d, t=log2(d);
while(m--) {
int u, v, w;
cin >> u >> v >> w;
to[u].pb(mp(v,w));
to[v].pb(mp(u,w));
}
memset(day,0x3f,sizeof(day));
while(k--) { int i; cin >> i; day[i]=pre[i]=0, q.push(i); }
up(i,0,d-1) cin >> lim[i], f[i][0]=lim[i];
up(j,1,t) up(i,0,d-1) if(i+(1<<j)-1<=d-1) f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);
while(q.size()) {
int x=q.front();
q.pop();
for(pii p:to[x]) {
int y=p.first, w=p.second;
if(pre[x]+w<=lim[day[x]]) {
if(day[x]>day[y]||day[x]==day[y]&&pre[x]+w>=pre[y]) continue;
day[y]=day[x], pre[y]=pre[x]+w, q.push(y);
}
else {
int now=query(day[x]+1,w);
if(now>d||now>day[y]||now==day[y]&&w>=pre[y]) continue;
day[y]=now, pre[y]=w, q.push(y);
}
}
}
up(i,1,n) {
int val=(day[i]+(bool)pre[i]<=d)?(day[i]+(bool)pre[i]):-1;
cout << val << ' ';
}
return 0;
}
浪潮sao
出成 \(n=5000\) 然后给全部询问的答案真的很有误导性,不懂为社么不出成交互题 /fad
首先可以通过 \(Q[1,i],Q[i,n]\) 求第一个/最后一个 \(\text{S}\) 的位置 \(l,r\)。
直觉 \(S\) 很有可能在前缀/后缀是严格众数,考虑 \(i\) 的合法性,若 \(Q[l,i]=Q[l,i-1]+1=Q[l+1,i]+1\) 或者 \(Q[i,r]=Q[i+1,r]+1=Q[i,r-1]\) 那么 \(i\) 是 \(\text{S}\)。考虑一下剩下的情况,首先左右的众数不同,那么只需要判断一下不是左右的众数即可,就是不能满足 \(Q[l+1,i-1]+1=Q[l+1,i],Q[i+1,r-1]+1=Q[i,r-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=1005;
int id, T, n, f[N][N], l, r, tag[N];
void mian() {
memset(tag,0,sizeof(tag));
memset(f,0,sizeof(f));
cin >> n, l=1, r=n;
up(i,1,n) up(j,i,n) cin >> f[i][j];
while(f[1][n]==f[l+1][n]) ++l;
while(f[1][n]==f[1][r-1]) --r;
tag[l]=tag[r]=1;
up(i,l+1,r-1) {
if(f[l][i]==f[l+1][i]+1&&f[l][i]==f[l][i-1]+1) tag[i]=1;
if(f[i][r]==f[i][r-1]+1&&f[i][r]==f[i+1][r]+1) tag[i]=1;
if(f[l+1][i-1]==f[l][i]&&f[i+1][r-1]==f[i][r]) tag[i]=1;
}
up(i,1,n) if(tag[i]) cout << i << ' '; cout << '\n';
}
signed main() {
freopen("letter.in","r",stdin);
freopen("letter.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> id >> T;
while(T--) mian();
return 0;
}
英雄传说hero
考虑快乐的英雄集合 \(A\),悲伤的英雄集合 \(B\),我们肯定拿 \(A\) 依次匹配 \(b\downarrow\) 的怪物,拿 \(B\) 依次匹配 \(b\uparrow\) 的怪物。这个把序列分成了前后缀(前缀怪物使高兴、后缀怪物使悲伤),那么前缀的状态可以用 \(f_{i,j}\) 即考虑到 \(i\) 还有 \(j\) 个怪物可以用来表示了(这个跟两种方向的英雄有等式关系),对前后缀 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)
using namespace std;
const int N=5005, M=10005, P=998244353;
int n, m, q, tmp, tot, tag[M], p[N], f[N][N], g[N][N], Ans[N];
inline void add(int &a,int b) { a=(a+b)%P; }
signed main() {
freopen("hero.in","r",stdin);
freopen("hero.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n, m=(n<<1);
up(i,1,n) cin >> tmp, tag[tmp]=1;
up(i,1,n) cin >> tmp, tag[tmp]=0;
up(i,1,m) if(tag[i]) p[++tot]=i;
f[0][0]=g[n+1][0]=1, p[n+1]=m+1;
up(i,1,n+1) {
int d=p[i]-p[i-1]-1;
up(j,0,n) if(f[i-1][j]) {
add(f[i][j+d],f[i-1][j]);
if(j+d-1>=0) add(f[i][j+d-1],f[i-1][j]);
}
}
dn(i,n,0) {
int d=p[i+1]-p[i]-1;
up(j,0,n) if(g[i+1][j]) {
add(g[i][j+d],g[i+1][j]);
if(j+d-1>=0) add(g[i][j+d-1],g[i+1][j]);
}
}
add(Ans[0],g[1][0]*tag[1]);
up(u,0,n) {
int l=p[u], r=p[u+1];
up(i,l+1,r-1) up(jl,0,l) {
int pl=jl+2*u-l, jr=pl-(r-i-1), pr=(m-r+1)-(n-u)-jr, k=pl+pr;
if(pl<0||pl>u||jr<0||jr>m-r+1||pr<0||pr>n-u) continue;
if(0<=k&&k<=n) add(Ans[n-k],f[u][jl]*g[u+1][jr]%P);
}
}
up(i,1,n) add(Ans[i],Ans[i-1]);
cin >> q;
while(q--) {
int l, r, ans;
cin >> l >> r;
ans=(Ans[r]-(l?Ans[l-1]:0))%P;
cout << (ans%P+P)%P << '\n';
}
return 0;
}
二叉竖bst
这是好题,顺便赞扬一下操作的 \(x\) 互不相同。
想想只给 bst 加数,查询的答案 \(x\) 怎么算。某个时刻加入 \(v\),若 \(v\leq x\),那么在这之后的 \(v'\in [1,v]\) 无用,若 \(v\geq x\),那么在这之后的 \(v'\in [v,\infty)\) 无用,意思就是 \(v\leq x\) 和 \(v\geq x\) 的部分相互独立,嗯之后只用进一步考虑 \(v\leq x\) 的做法,另一侧是一致的,按照时间顺序能加能加出一个 \(v\uparrow\) 的序列,
题目维度很多,先考虑枚举个什么,枚举 \(t\) 像疯了,枚举 \(v\) 要动态加 \((l,r,t,v)\) 和查询 \(u\),枚举 \(u\) 只需要动态加删 \((t,x)\) 查询 \((T,X)\),枚举 \(u\) 看着最有道理,之后完全可以扔掉 \(u\) 了。动态加删考虑线段树,以 \(t\) 为下标 \(x\) 为权值建立的线段树要在前缀上查询 \(<X\) 的答案,对于每个点都有好多种情况感觉维护不了,以 \(x\) 为下标 \(t\) 为权值建立线段树,要做对前缀查询 \(<T\) 的答案,首先每个点的原始贡献是确定唯一且可以合并的,合并的时候写个对 \(low\) 的查询即可,先确定右边再确定就好了,查询就类似了,写代码注意用脑子。
#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 ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define pii pair<int,int>
#define mp make_pair
#define low first
#define val second
using namespace std;
const int N=600005, M=2400005;
int n, m, xa, xb, tot, sp[N], Ans[N];
int ans[M], con[M], low[M];
struct node { int u, t, x; } a[N], b[N];
pii query(int l,int r,int t,int p=1,int s=1,int e=tot) {
if(l>r) return mp(0,0);
int mid=(s+e)>>1;
if(l<=s&&e<=r) {
if(!low[p]||low[p]>t) return mp(0,0);
if(s==e) {
return mp(low[p],ans[p]);
}
if(!low[rs(p)]||t<low[rs(p)]) {
pii ret=query(l,r,t,ls(p),s,mid);
return ret;
}
pii ret=query(l,r,t,rs(p),mid+1,e);
return mp(low[ls(p)]?min(low[ls(p)],ret.low):ret.low,con[p]+ret.val);
}
pii x=mp(0,0), y=mp(0,0);
if(r>mid) x=query(l,r,t,rs(p),mid+1,e);
if(l<=mid) y=query(l,r,x.low?x.low:t,ls(p),s,mid);
return mp(y.low?y.low:x.low,x.val+y.val);
}
inline void tup(int p,int s,int e) {
if(low[ls(p)]&&low[rs(p)]) low[p]=min(low[ls(p)],low[rs(p)]);
else low[p]=low[ls(p)]+low[rs(p)];
int mid=(s+e)>>1;
if(low[rs(p)]) con[p]=query(s,e,low[rs(p)],ls(p),s,mid).val;
else con[p]=query(s,e,m+1,ls(p),s,mid).val;
ans[p]=con[p]+ans[rs(p)];
}
void updata(int real,int x,int t,int p=1,int s=1,int e=tot) {
if(s==e) {
ans[p]=real, low[p]=t, con[p]=0;
return;
}
int mid=(s+e)>>1;
if(x<=mid) updata(real,x,t,ls(p),s,mid);
if(x>mid) updata(real,x,t,rs(p),mid+1,e);
tup(p,s,e);
}
signed main() {
freopen("bst.in","r",stdin);
freopen("bst.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
up(i,1,m) {
int opt, l, r, x;
cin >> opt;
if(opt==1) {
cin >> l >> r >> x;
a[++xa]=(node){l,i,x};
a[++xa]=(node){r+1,i,-x};
sp[++tot]=x;
Ans[i]=-1;
}
else {
cin >> l >> x;
b[++xb]=(node){l,i,x};
sp[++tot]=x;
}
}
sort(sp+1,sp+1+tot), tot=unique(sp+1,sp+1+tot)-sp-1;
up(i,1,xa) {
bool flag=a[i].x<0; a[i].x=abs(a[i].x);
a[i].x=lower_bound(sp+1,sp+1+tot,a[i].x)-sp;
if(flag) a[i].x=-a[i].x;
}
up(i,1,xb) b[i].x=lower_bound(sp+1,sp+1+tot,b[i].x)-sp;
sort(a+1,a+1+xa,[](node i,node j){return i.u<j.u;});
sort(b+1,b+1+xb,[](node i,node j){return i.u<j.u;});
map<int,int> ins;
int j=1;
up(i,1,xb) {
while(j<=xa&&a[j].u<=b[i].u) {
if(a[j].x>0) {
updata(sp[a[j].x],a[j].x,a[j].t);
ins[a[j].x]=a[j].t;
}
else {
updata(0,-a[j].x,0);
ins[-a[j].x]=0;
}
++j;
}
Ans[b[i].t]=query(1,b[i].x,b[i].t).val;
if(ins[b[i].x]&&ins[b[i].x]<b[i].t) Ans[b[i].t]-=sp[b[i].x];
}
memset(ans,0,sizeof(ans));
memset(con,0,sizeof(con));
memset(low,0,sizeof(low));
j=1;
up(i,1,xb) {
while(j<=xa&&a[j].u<=b[i].u) {
if(a[j].x>0) updata(sp[a[j].x],tot-a[j].x+1,a[j].t); else updata(0,tot+a[j].x+1,0);
++j;
}
int sav=query(1,tot-b[i].x+1,b[i].t).val;
Ans[b[i].t]+=sav;
}
up(i,1,m) if(Ans[i]>=0) cout << Ans[i] << '\n';
return 0;
}