過分的神聖,往往比惡魔更加惡質。
前言
最大的一個收獲就是不要動不動就碼線段樹,一定要審清楚題目之后再碼!!
T1 一開始理解錯題了,以為答案是就是 \(\dfrac{\operatorname{len}(s,t)}{k}\) 然后就傻呵呵地碼完了,然后看了看整個機房都沒有什么動靜,才發現自己的無知。
后來就是換做法,以為是 線段樹+樹鏈剖分 ,想了好久的區間合並,最后想的差不多了,就開始碼線段樹,剛剛碼完就發現直接 前綴和+lower_bound
就可以解決這個問題,主要是這個思路還是錯的。。。
比較成功的就是 20min 碼完了 T3 的小暴力,然后稍微調了兩下就過了。。
T1 第零題
解題思路
有一個結論:
對於⼀條鏈,如果體⼒都是滿的從兩邊開始⾛,那么復活的次數是一樣的
證明的話,這里只證明一下比較難的部分:一條全部由 \(<k\) 的價值構成的鏈。
假設現在有一條總和在 \((k,2k)\) 之間的鏈,並且滿足上述條件,那么加入說我們把起始點向另一端移一條邊(保證鏈長依舊大於 k ),那么最后的復活次數還是 1 。
同樣的,擴展到一個總和未知的鏈上,它也一定可以分成一段 \(<k\) 的和若干的長度 \(\ge k\) 的段,通過上述操作最終的復活次數還是一樣的。
那么我們就可以以 LCA 為中心,對於這條鏈的左右兩半部分分別向兩端移動,最后計算中間的部分。
然后就可以通過倍增進行維護。
對於一個點記錄這個點到根節點上的路徑的各個點到根節點的距離以及對應的序號,然后在這個數組上二分就可以得到從這個點滿狀態開始向上的第一個死亡點了。
倍增數組維護就不用說了吧。。
然后對於起點 s 直接向上倍增跳復活數組,最后得到的就是到 LCA 的距離最大且距離 \(<k\) 的點。
接下來在 t 到 LCA 的那一條鏈上跳祖先直到到 LCA 的距離與前面求的剩余距離之和 \(\ge k\) 的點就是我們要移動的那一段,判斷這一段是否 \(\ge k\) 就好了。
對於 t 到 LCA 鏈上剩下的點操作和 s 的那條鏈相差無幾。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=2e5+10;
int n,m,q,top,sta[N],st[N],s[N],f[N][25];
int tot=1,head[N],ver[N<<1],nxt[N<<1],edge[N<<1];
int tim,dfn[N],id[N],son[N],siz[N],dep[N],dis[N],topp[N],fa[N];
int die[N][25];
void add_edge(int x,int y,int val)
{
ver[++tot]=y;
edge[tot]=val;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x)
{
siz[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(siz[to]) continue;
dis[to]=dis[x]+edge[i];
dep[to]=dep[x]+1;
fa[to]=x; s[to]=edge[i];
f[to][0]=x;
dfs1(to);
siz[x]+=siz[to];
if(siz[to]>siz[son[x]])
son[x]=to;
}
}
void dfs2(int x,int tp)
{
dfn[x]=++tim;
id[tim]=x;
topp[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=nxt[i])
if(!dfn[ver[i]])
dfs2(ver[i],ver[i]);
}
int LCA(int x,int y)
{
while(topp[x]^topp[y])
{
if(dep[topp[x]]<dep[topp[y]])
swap(x,y);
x=fa[topp[x]];
}
if(dep[x]>dep[y])
swap(x,y);
return x;
}
void dfs3(int x)
{
if(dis[x]-dis[fa[x]]>=m) die[x][0]=fa[x];
else
{
int pos=upper_bound(sta+1,sta+top+1,dis[x]-m)-sta-1;
if(dis[x]-sta[pos]<m) die[x][0]=0;
else die[x][0]=st[pos];
}
sta[++top]=dis[x]; st[top]=x;
for (int i=0;die[x][i];i++)
die[x][i+1]=die[die[x][i]][i];
for (int i=0;f[x][i];i++)
f[x][i+1]=f[f[x][i]][i];
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
if(to==fa[x]) continue;
dfs3(to);
}
top--;
}
signed main()
{
n=read(); m=read();
for(int i=1,x,y,val;i<n;i++)
x=read(),y=read(),val=read(),
add_edge(x,y,val),add_edge(y,x,val);
dfs1(1); dfs2(1,1); dfs3(1);
q=read();
while(q--)
{
int x,y,lca,val,temp,ans=0;
x=read(); temp=y=read(); lca=LCA(x,y);
for(int i=20;i>=0;i--)
if(die[x][i]&&dep[die[x][i]]>=dep[lca])
x=die[x][i],ans+=(1ll<<i);
val=dis[x]-dis[lca];
for(int i=20;i>=0;i--)
if(f[temp][i]&&dis[f[temp][i]]-dis[lca]>=m-val)
temp=f[temp][i];
if(dis[temp]+dis[x]-2*dis[lca]>=m) ans++;
for(int i=20;i>=0;i--)
if(die[y][i]&&dep[die[y][i]]>=dep[temp])
y=die[y][i],ans+=(1ll<<i);
printf("%lld\n",ans);
}
return 0;
}
T2 第負一題
解題思路
官方題解又雙叒叕不說人話。。。
對於一個區間 \([l,r]\) 我們顯然可以運用類似於 沒有上司的舞會 的思路 \(\mathcal{O}(r-l+1)\) 求出來。
DP 轉移柿子就是 \(f_{i,0}=\max(f_{i-1,0},f_{i-1,1}),f_{i,1}=f_{i-1,0}+s_i\)
對於正解的話,二分
對於每一個二分到的區間分成 \(L,R\) 兩個部分,對於每半個區間維護兩個值,\(fl_{i,0/1},fr_{i,0/1}\)
\(fl_{i,0}\) 表示默認不選擇 \(mid\) 這個元素的在 \([i,mid]\) 的范圍內可以得到的最大值。
\(fl_{i,1}\) 表示默認選擇 \(mid\) 這個元素的在 \([i,mid]\) 的范圍內可以得到的最大值。
\(fr\) 數組的定義類似,只不過范圍是 \([mid+1,i]\)
然后對於位於 \(L\) 的 \(i\) 以及位於 \(R\) 的 \(j\),在 \([i,j]\) 區間的最大值就是:
那么我們假設 \(ld_i=\max(0,fl_{i,1}-fl_{i,0}),rd_i=\max(0,fr_{i,1}-fr_{i,0})\)
然后對於區間 \([i,j]\) 的答案就是 \(fl_{i,0}+fr_{j,0}+\max(ld_i,rd_j)\)
每個 \(ld_i\) 以及 \(rd_j\) 的貢獻就可以直接通過乘上另一半的區間長度來算。
那么問題就變為了求 \(\sum\limits_{i=l}^{mid}\sum\limits_{j=mid+1}^r \max(ld_i,rd_j)\)
我們先將 \(ld,rd\) 數組從小到大進行排序,掃描 \(L\) 部分,另外用一個指針 \(pos\) 維護 \(R\) 部分。
保證 \([mid+1,pos-1]\) 都是小於 \(ld_i\) 的,由此可以算出每一個 \(ld_i\) 的貢獻。
在維護 \(pos\) 指針的同時統計當前的 \(rd_{pos}\) 的貢獻,計算方式類似於上面的 \(ld_i\) 。
最后不要忘了把指針一直算到右端點就好了。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=2e5+10,mod=998244353,INF=1e18;
int n,ans,s[N],fl[N][2],fr[N][2],f[N][2],ld[N],rd[N];
void Binary(int l,int r)
{
if(l==r) return ans=(ans+s[l])%mod,void();
int mid=(l+r)>>1,pos=mid+1;
Binary(l,mid); Binary(mid+1,r);
f[mid][0]=-INF; f[mid][1]=s[mid]; fl[mid][1]=s[mid];
for(int i=mid-1;i>=l;i--)
{
f[i][0]=max(f[i+1][0],f[i+1][1]);
f[i][1]=f[i+1][0]+s[i];
fl[i][1]=max(f[i][0],f[i][1]);
}
f[mid][0]=0; f[mid][1]=-INF; fl[mid][0]=0;
for(int i=mid-1;i>=l;i--)
{
f[i][0]=max(f[i+1][0],f[i+1][1]);
f[i][1]=f[i+1][0]+s[i];
fl[i][0]=max(f[i][0],f[i][1]);
}
for(int i=mid;i>=l;i--)
{
ld[i]=max(0ll,fl[i][1]-fl[i][0]);
ans=(ans+fl[i][0]*(r-mid))%mod;
}
f[mid+1][0]=-INF; f[mid+1][1]=s[mid+1]; fr[mid+1][1]=s[mid+1];
for(int i=mid+2;i<=r;i++)
{
f[i][0]=max(f[i-1][0],f[i-1][1]);
f[i][1]=f[i-1][0]+s[i];
fr[i][1]=max(f[i][1],f[i][0]);
}
f[mid+1][0]=0; f[mid+1][1]=-INF; fr[mid+1][0]=0;
for(int i=mid+2;i<=r;i++)
{
f[i][0]=max(f[i-1][0],f[i-1][1]);
f[i][1]=f[i-1][0]+s[i];
fr[i][0]=max(f[i][1],f[i][0]);
}
for(int i=mid+1;i<=r;i++)
{
rd[i]=max(0ll,fr[i][1]-fr[i][0]);
ans=(ans+fr[i][0]*(mid-l+1))%mod;
}
sort(ld+l,ld+mid+1); sort(rd+mid+1,rd+r+1);
for(int i=l;i<=mid;i++)
{
while(pos<=r&&rd[pos]<=ld[i]) ans=(ans+rd[pos]*(i-l))%mod,pos++;
ans=(ans+(pos-mid-1)*ld[i])%mod;
}
while(pos<=r)ans=(ans+rd[pos]*(mid-l+1))%mod,pos++;
}
signed main()
{
n=read(); for(int i=1;i<=n;i++) s[i]=read();
Binary(1,n); printf("%lld",ans);
return 0;
}
T3 第負二題
解題思路
打了一個 \(\mathcal{O}(n^2)\) 的假做法,被 zero4338 Hack 了。
現在正在努力地學習 \(\mathcal{O}(n)\) 做法。
upd on 9.11 現在我學會了。。。
單調隊列維護每一行上面的轉移來源,以及下面的轉移來源。
維護對於比上一行早一個時間,同一個時間以及晚一個時間的情況,正確性顯然。
單調隊列可以做到 \(\mathcal{O}(n)\) 。
code
原測試數據可以切掉的 n^2 算法
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=5e6+10,mod=998244353;
int n,m,tim,cnt,out,L,X,Y,Ans;
ull A,B;
int l[N],r[N],lasl[N],lasr[N],ans[N];
bool las[N];
ull xorshift128p() {
ull T = A, S = B;
A = S;
T ^= T << 23;
T ^= T >> 17;
T ^= S ^ (S >> 26);
B = T;
return T + S;
}
void init() {
for (int i = 1; i <= n; i ++) {
l[i] = xorshift128p() % L + X;
r[i] = xorshift128p() % L + Y;
if (l[i] > r[i]) swap(l[i], r[i]);
}
}
signed main()
{
cnt=n=read();L=read();X=read();Y=read();scanf("%llu%llu",&A,&B);init();
las[0]=las[n+1]=true;
for(int i=1;i<=n/2+(n&1)&&cnt;i++)
{
for(int j=i-1;j<=n-i+2;j++)
if(!ans[j]) lasl[j]=l[j],lasr[j]=r[j];
else las[j]=true;
for(int j=i;j<=n-i+1;j++)
{
if(ans[j]) continue;
if(las[j-1]||las[j+1]){ans[j]=i;cnt--;continue;}
l[j]=max(l[j]+1,max(lasl[j-1],lasl[j+1]));
r[j]=min(r[j]-1,min(lasr[j-1],lasr[j+1]));
if(l[j]>r[j]) ans[j]=i,cnt--;
}
}
for(int i=1,base=1;i<=n;i++,base=base*3%mod)
Ans=(Ans+base*ans[i]%mod)%mod;
printf("%lld",Ans);
return 0;
}
正解
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=5e6+10,mod=998244353,INF=1e18;
int n,tim,ans=1,l[N],r[N];
int poi[6];
struct Deque
{
int head=1,tail=0,num[N<<1];
void push(int x){num[++tail]=x;}
void pop_front(){head++;}
void pop_back(){tail--;}
bool empty(){return head>tail;}
int front(){return num[head];}
int back(){return num[tail];}
int second(){return num[head+1];}
}q[5];
ull xorshift128p(ull &A, ull &B){
ull T = A, S = B;
A = S;
T ^= T << 23;
T ^= T >> 17;
T ^= S ^ (S >> 26);
B = T;
return T + S;
}
void init(){
int L,X,Y;ull A,B;
n=read(); L=read(); X=read(); Y=read(); scanf("%llu%llu",&A,&B);
for (int i = 1; i <= n; i ++) {
l[i] = xorshift128p(A, B) % L + X;
r[i] = xorshift128p(A, B) % L + Y;
if (l[i] > r[i]) swap(l[i], r[i]);
}
}
int upd(int pos,int x)
{
if(pos==1) return l[x]+x;
if(pos==2) return r[x]-x;
if(pos==3) return l[x]-x;
return r[x]+x;
}
void update(int pos,int L,int R)
{
int sym=((pos&1)?1:-1);
while(poi[pos]<=R)
{
while(!q[pos].empty()&&sym*upd(pos,q[pos].back())<=sym*upd(pos,poi[pos])) q[pos].pop_back();
q[pos].push(poi[pos]); poi[pos]++;
}
while(!q[pos].empty()&&q[pos].front()<L) q[pos].pop_front();
}
signed main()
{
init();
poi[1]=poi[2]=poi[3]=poi[4]=1;
for(int i=1;i<=4;i++) update(i,1,1);
for(int i=2,base=3,flag;i<=n;i++,base=base*3%mod)
{
flag=false; update(1,i-tim-1,i); update(2,i-tim-1,i);
update(3,i,max(i,i+tim-1)); update(4,i,min(i,i-tim+1));
int tmpl=i-tim-1,tmpr=i+tim+1,mnl=i-tim,mnr=i+tim,recl=i-tim+1,recr=i+tim-1,mn1,mn2,mn3,mn4;
int tmp1=upd(1,q[1].front()),tmp2=upd(2,q[2].front()),tmp3,tmp4,l1,l2,r1,r2;
if(!q[1].empty()&&q[1].front()>=mnl) mn1=upd(1,q[1].front());
else mn1=upd(1,q[1].second());
if(!q[2].empty()&&q[2].front()>=mnl) mn2=upd(2,q[2].front());
else mn2=upd(2,q[2].second());
mn3=max(upd(3,q[3].front()),upd(3,mnr));
mn4=min(upd(4,q[4].front()),upd(4,mnr));
tmp3=max(mn3,upd(3,tmpr)); tmp4=min(mn4,upd(4,tmpr));
if(tmp1+tim-i+1<=tmp2-tim+i-1&&tmp3+tim+i+1<=tmp4-tim-i-1)
if(max(tmp1+tim-i+1,tmp3+tim+i+1)<=min(tmp2-tim+i-1,tmp4-tim-i-1))
{
flag=true; tim++;
update(1,tmpl,i); update(2,tmpl,i);
update(3,i,tmpr); update(4,i,tmpr);
}
if(!flag&&mn1+tim-i<=mn2-tim+i&&mn3+tim+i<=mn4-tim-i)
if(max(mn1+tim-i,mn3+tim+i)<=min(mn2-tim+i,mn4-tim-i))
{
flag=true;
update(1,mnl,i); update(2,mnl,i);
update(3,i,mnr); update(4,i,mnr);
}
if(!flag)
{
tim--;
update(1,recl,i); update(2,recl,i);
update(3,i,recr); update(4,i,recr);
}
ans=(ans+base*(tim+1))%mod;
}
printf("%lld",ans);
return 0;
}