題目描述
有 \(n\) 個元素 \(\\),\(m\) 次詢問 \([l_i,r_i]\),問滿足 \(l_i\leq x的三元組 \((x,y,z)\),其最大的 \(a_x+a_y+a_z\) 是最少。
\(n,m\leq 5\cdot 10^5,a_i\leq 10^9\)
解法
首先考慮 \(m=1\) 怎麼做?乙個關鍵的 \(\tt observation\) 是:只有滿足 \(a_x>a_,a_...a_\),\(a_y>a_,a_...a_\) 的 \((x,y)\) 才是可能對答案有貢獻的,並且這樣的二元組只有 \(o(n)\) 對。
那麼我們可以從後往前列舉 \(x\),並且維護單調棧,那麼 \(y\) 只可能是單調棧被彈出的元素和棧頂的元素,那麼知道 \(x,y\) 之後 \(z\) 就是字尾最大值,這樣就成功解決了 \(m=1\) 的情況。
回到本題,我們把詢問掛在左端點處,只需要對 \(z\) 維護線段樹,就可以把 \(z\) 的範圍限制在 \([l_i,r_i]\),時間複雜度 \(o(n\log n)\)
總結
找結論的乙個方向是,考慮可能貢獻的元素。本題因為確定 \(x,y\) 之後才能確定 \(z\),所以必須要找結論。
此外列舉的量不要侷限,我一開始列舉 \(y\) 怎麼都想不出來,要是列舉 \(x\) 這題可能會好做得多。
單調棧的應用不止有處理 \(\max/\min\) 的功能,還可以幫助你只考慮有貢獻的資訊。
#include #include #include using namespace std;
const int m = 500005;
const int n = m<<2;
#define int long long
int read()
while(c>='0' && c<='9')
return x*f;
}void write(int x)
int n,m,tp,a[m],b[n],s[m],ans[m],tr[n],mx[n];
struct node;vectorq[m];
void build(int i,int l,int r)
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
tr[i]=b[i]=max(b[i<<1],b[i<<1|1]);
}void zxy(int i,int c)
void upd(int i,int l,int r,int l,int r,int c)
int mid=(l+r)>>1;
zxy(i<<1,mx[i]);zxy(i<<1|1,mx[i]);
upd(i<<1,l,mid,l,r,c);
upd(i<<1|1,mid+1,r,l,r,c);
tr[i]=max(tr[i<<1],tr[i<<1|1]);
}void add(int x,int y)
int ask(int i,int l,int r,int l,int r)
signed main()
); }
for(int i=n;i>=1;i--)
for(int i=1;i<=m;i++)
write(ans[i]),puts("");
}
題目描述
給定 \(n\) 個點 \(m\) 條邊的無向圖,邊權為 \(1\),點 \(i\) 可以花費 \(c_i\) 的代價到達所有距離 \(\leq d_i\) 的城市。保證圖聯通,求出 \(1\) 到所有點的最短路。
\(n\leq 2\cdot 10^5,c_i\leq 10^9,n-1\leq m\leq n+50\)
解法
首先考慮樹的情況怎麼做,顯然的思路是點分治優化建圖,然後跑最短路即可。
那麼對於圖的情況,我們先任取一棵生成樹,然後對其點分治優化建圖。現在非樹邊是沒有考慮到的,我們只需要讓路徑強制經過非樹邊的某個端點即可,所以我們以取非樹邊的乙個端點為根 \(\tt bfs\),然後優化建圖即可。
時間複雜度 \(o(n\log ^2n+nk\log n)\)
為了去掉複雜度中的 \(o(\log n)\) 其實有更好的方法,由於代價是點代價,所以每個點只會被訪問一次,那麼樹的情況我們可以不把圖顯式地建出來,在點分樹上維護指標即可做到 \(o(n\log n)\);\(\tt bfs\) 也可以類似地維護指標,時間複雜度 \(o(n\log n+nk)\)
下面貼上我深度卡常的法 \(1\) **,注意這個**只能獲得 \(80\) 分。
總結
點代價的最短路有其特殊性(訪問即確定,只會訪問一次),一定要注意。
#pragma gcc optimize("ofast")
#include #include #include #include #include #include #include using namespace std;
const int m = 200005;
const int n = 80*m;
#define pb push_back
#define ll long long
#define pii pairint read()
while(c>='0' && c<='9')
return x*f;
}void write(ll x)
int n,m,rt,sz,a[m],b[m],c[m],d[m],vis[m],mx[m],siz[m];
vectorg[m],g[m];int cnt,m1,m2,tot,f[n];ll dis[n];
struct edgee[n*3];
struct node
};void pre(int u,int fa)
pre(v,u);g[u].pb(v);g[v].pb(u); }}
void find(int u,int fa)
mx[u]=max(mx[u],sz-siz[u]);
if(mx[u]=d) b[++m2]=;
for(int v:g[u]) }
void add(int u,int v)
,f[u]=tot;
}void work()
if(x) add(b[i].u,x);
if(x!=lst && lst) add(x,lst);lst=x; }}
void solve(int u)
}void bfs(int s)
; if(b[u]>=d[u]) b[++m2]=;
for(int v:g[u]) if(d[v]==-1)
d[v]=d[u]+1,q.push(v); }}
void dijk()
); while(!q.empty())
); }
} }}signed main()
for(int i=1;i<=n;i++)
b[i]=read(),c[i]=read();
pre(1,0);
//work for tree
memset(vis,0,sizeof vis);
mx[rt=0]=sz=n;find(1,0);solve(1);
//work for graph
for(int i=1;i<=n;i++) if(a[i])
dijk();
for(int i=2;i<=n;i++)
write(dis[i]),puts("");
}
省選集訓2022 模擬賽6
題目描述 定義長度為 n 的好串 s 滿足 給你長度為 n 的序列 a 和 v 分別表示原序列和價值序列。你每次可以選擇乙個原序列中的好串,將其刪除之後剩下的串會前後拼接。設這次刪除的長度是 l 那麼會得分 v l 問最大得分,不一定要把原序列刪完。n leq 400,v i leq 10 5,a ...
省選集訓2022 模擬賽8
題目描述 給定乙個 n times m 的 01 矩陣,對於矩陣的每乙個位置,你需要對於這個位置上的值反轉,然後求出這個矩陣的秩的變化 0 可以將這個矩陣看成 n 個大小為 0,2 m 的數,秩就是它們線性基的大小。n,m leq 1000 解法 我們判斷求出原先的 n 個向量在原來的線性基中是可以...
省選集訓2022 模擬賽10
題目描述 給定 n 個元素,每個元素有兩個屬性值 a i,b i 我們可以將其以任意順序排列,要最大化下式 min a i i cdot k max b i i cdot k n leq 10 5,a i,b i,kn leq 10 9 解法 應該是遇到困難退大火才對,直接使用列舉法,考慮列舉 m ...