[ABC369C] Count Arithmetic Subarrays
題意:
判斷有多少個區間是等差數列(不能重排)。
\(1 \le n \times 10^5\)。
思路:
賽時看錯題了,以為這個區間可以重排,卡了 8min,小丑了。
首先容易注意到,對於一個區間 \([l,r]\),若其是等差數列,則這個區間的子區間 \([l',r']\) 肯定也是子序列,造成的貢獻是 \(\frac{(r-l+1)(r-l+2)}{2}\)。
那麼考慮求出所有極長等差區間,設 \(d_i = a_{i+1} - a_i\),若 \(i\) 能加入 \(i-1\) 的等差區間,當且僅當 \(d_i = d_{i-1}\);否則就要新開一個等差子區間。
注意最後答案要加 \(n-1\)。
時間複雜度為 \(O(N)\)。
完整程式碼:
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(A) memset(A,0,sizeof(A))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
#define For(i,l,r) for(int i=l;i<=r;i++)
#define _For(i,l,r) for(int i=r;i>=l;i--)
using namespace std;
typedef long double lb;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const int N=2e5+6;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
ll n,s,ans;
ll a[N],d[N];
int main(){
n=read();
For(i,1,n)
a[i]=read();
For(i,1,n)
d[i]=a[i+1]-a[i];
For(i,1,n){
if(d[i]!=d[i-1]){
ans+=s*(s+1)/2;
s=1;
}
else
s++;
}
ans+=s*(s+1)/2;
write(ans+n-1);
return 0;
}
[ABC369D] Bonus EXP
題意:
有 \(n\) 個物品依次從你身邊經過,第 \(i\) 個物品的貢獻為 \(a_i\)。
若你選擇的第 \(j\) 個物品是 \(i\):
-
當 \(j\) 為偶數時:收益為 \(a_i \times 2\)。
-
否則收益為 \(a_i\)。
問最大收益。
\(1 \le n \times 10^5\)。
思路:
考慮動態規劃演算法。
注意到當前位置的取值只跟當前取的個數的奇偶性有關,則定義 \(dp_{i,0/1}\) 表示對於前 \(i\) 個物品取的物品個數對 \(2\) 取餘的結果為 \(0/1\) 的最大權值,則狀態轉移方程為:
最後的答案是 \(\max(dp_{n,0},dp_{n,1})\),時間複雜度為 \(O(N)\)。
完整程式碼:
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
#define For(i,l,r) for(int i=l;i<=r;i++)
#define _For(i,l,r) for(int i=r;i>=l;i--)
using namespace std;
typedef long double lb;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=2e5+10;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
ll n;
ll a[N],dp[N][2];
int main(){
n=read();
For(i,1,n)
a[i]=read();
dp[1][1]=a[1],dp[1][0]=0;
For(i,2,n){
dp[i][0]=max(dp[i-1][1]+a[i]*2,dp[i-1][0]);
dp[i][1]=max(dp[i-1][0]+a[i],dp[i-1][1]);
}
write(max(dp[n][0],dp[n][1]));
return 0;
}
[ABC369E] Sightseeing Tour
題意:
給定一個 \(n\) 個節點的無向圖,共 \(q\) 次詢問,每次給定 \(k\) 個必須經過的邊,問從 \(1 \sim n\) 的最短路徑是多少。
\(1 \le n \le 500,1 \le q \le 3000,1 \le k \le 5\)。
思路:
考慮先求出任意兩點的最短路徑 \(dis_{i,j}\),因為 \(n \le 500\),可以使用 Floyd 演算法 \(n^3\) 跑出。
然後對於每組詢問,必須要經過編號為 \(a_1,a_2,a_3,\cdots,a_k\) 的橋,定義編號為 \(i\) 的橋為 \((u_i,v_i,w_i)\)。
同時注意到 \(k \le 5\) 非常小,考慮全排列出依次經過哪些橋,同時爆搜是先到達這個橋的左端還是右端即可。
時間複雜度為 \(O(N^3 + QK!2^K)\)。
完整程式碼:
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
#define For(i,l,r) for(int i=l;i<=r;i++)
#define _For(i,l,r) for(int i=r;i>=l;i--)
using namespace std;
typedef long double lb;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=505,M=2e5+10;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
ll n,m,q,k,x,y,w,ans;
ll a[N],p[N],A[M],B[M],W[M];
ll dis[N][N];
void dfs(ll pos,ll pre,ll sum){
if(sum>ans)
return ;
if(pos==k+1){
ans=min(ans,sum+dis[pre][n]);
return ;
}
dfs(pos+1,A[a[p[pos]]],sum+dis[pre][B[a[p[pos]]]]+W[a[p[pos]]]);
dfs(pos+1,B[a[p[pos]]],sum+dis[pre][A[a[p[pos]]]]+W[a[p[pos]]]);
}
int main(){
n=read(),m=read();
For(i,1,n){
For(j,1,n){
if(i==j)
continue;
dis[i][j]=1e18;
}
}
For(i,1,m){
x=read(),y=read(),w=read();
dis[x][y]=min(dis[x][y],w);
dis[y][x]=min(dis[y][x],w);
A[i]=x,B[i]=y,W[i]=w;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
q=read();
while(q--){
ans=1e18;
k=read();
For(i,1,k){
a[i]=read();
p[i]=i;
}
while(1){
dfs(1,1,0);
if(!next_permutation(p+1,p+k+1))
break;
}
write(ans);
putchar('\n');
}
return 0;
}
[ABC369F] Gather Coins
題意:
給定一個 \(h \times w\) 的棋牌,有 \(n\) 個位置上有一個棋子,其它位置沒有棋子,人每次可以往下或往左走,問從 \((1,1)\) 走到 \((n,n)\) 能收集到的最多的棋子數量。
\(1 \le h,w \le 2 \times 10^5,1 \le n \le \min(hw-2,2 \times 10^5)\)。
思路:
考慮動態規劃,令 \(dp_i\) 表示到達第 \(i\) 個棋子最大權值,則狀態轉移方程為:
這是二維偏序最佳化動態規劃問題,考慮先按 \(x\) 從小到大排序,若 \(x\) 相同則按 \(y\) 從小到大排序,那麼這樣對於 \(j < i\),自然都滿足 \(x_j \le x_i\),則狀態轉移方程降了一維:
相當於詢問 \([1,y_i]\) 區間內的最大值,因為需要輸出路徑,所以我們還需要記錄一下最大值的位置,使用線段樹維護即可。
時間複雜度為 \(O(N \log N)\)。
完整程式碼:
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
#define For(i,l,r) for(int i=l;i<=r;i++)
#define _For(i,l,r) for(int i=r;i>=l;i--)
using namespace std;
typedef long double lb;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=2e5+10;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
struct Node{
ll x,y;
bool operator<(const Node&rhs)const{
if(x^rhs.x)
return x<rhs.x;
return y<rhs.y;
}
}a[N];
struct St{
ll l,r;
ll Max,id;
}X[N<<2];
ll h,w,n,id,cnt,ans;
ll dp[N],pre[N];
string S[N];
void pushup(ll k){
X[k].Max=max(X[k<<1].Max,X[k<<1|1].Max);
if(X[k].Max==X[k<<1].Max)
X[k].id=X[k<<1].id;
else
X[k].id=X[k<<1|1].id;
}
void build(ll k,ll l,ll r){
X[k].l=l,X[k].r=r;
if(l==r)
return ;
ll mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void add(ll k,ll i,ll v,ll id){
if(X[k].l==i&&i==X[k].r){
if(v>X[k].Max){
X[k].Max=v;
X[k].id=id;
}
return ;
}
ll mid=(X[k].l+X[k].r)>>1;
if(i<=mid)
add(k<<1,i,v,id);
else
add(k<<1|1,i,v,id);
pushup(k);
}
pi query(ll k,ll l,ll r){
if(X[k].l==l&&r==X[k].r)
return {X[k].Max,X[k].id};
ll mid=(X[k].l+X[k].r)>>1;
if(r<=mid)
return query(k<<1,l,r);
else if(l>mid)
return query(k<<1|1,l,r);
else{
auto x=query(k<<1,l,mid),y=query(k<<1|1,mid+1,r);
pi ans;
ans.fi=max(x.fi,y.fi);
if(ans.fi==x.fi)
ans.se=x.se;
else
ans.se=y.se;
return ans;
}
}
int main(){
h=read(),w=read(),n=read();
a[0]={1,1};
a[n+1]={h,w};
For(i,1,n)
a[i]={read(),read()};
build(1,1,w);
sort(a+1,a+n+1);
dp[1]=ans=id=1;
add(1,a[1].y,1,1);
For(i,2,n){
auto t=query(1,1,a[i].y);
dp[i]=t.fi+1;
pre[i]=t.se;
add(1,a[i].y,dp[i],i);
if(dp[i]>ans){
ans=dp[i];
id=i;
}
}
pre[n+1]=id;
id=n+1;
while(1){
ll x=pre[id];
++cnt;
ll sx=a[x].x,sy=a[x].y,tx=a[id].x,ty=a[id].y;
For(i,1,tx-sx)
S[cnt]+='D';
For(i,1,ty-sy)
S[cnt]+='R';
id=x;
if(!x)
break;
}
write(ans);
putchar('\n');
_For(i,1,cnt)
for(auto c:S[i])
putchar(c);
return 0;
}
[ABC369G] As far as possible
題意:
給定一個 \(n\) 個節點且有邊權的樹,對於每個 \(k \in [1,n]\) 求:
- 選取 \(k\) 條從根結點出發到葉子結點的簡單路徑,求這些路徑的並集上所有結點的點權之和的最大值。
\(1 \le n \le 2 \times 10^5\)。
思路:
雙倍經驗。
明顯有一個貪心的思路:
-
每次取葉子到根的路徑權值和最大的。
-
清空該鏈上的點的點權。
模擬 \(n\) 次即可,但是複雜度不可取,考慮資料結構最佳化。
將葉子節點按 dfn 序從小到大編號,令 \(l_u,r_u\) 表示 \(u\) 子樹內葉子節點的編號在 \([l_u,r_u]\) 間。
每次找到路徑權值和最大的那個葉子節點,可以直接暴力跳父親清空路徑上的點,每次清空就是將區間 \([l_u,r_u]\) 內的葉子結點到根的權值和減去 \(w_u\)。
若遇到一個點已經被清空了,故該點到根節點路徑上的點肯定也被清空了,就可以直接退出了;這樣每個點做多隻會清空一次。
考慮使用線段樹維護上述操作,時間複雜度均攤為 \(O(N \log N)\)。
完整程式碼:
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
#define For(i,l,r) for(int i=l;i<=r;i++)
#define _For(i,l,r) for(int i=r;i>=l;i--)
using namespace std;
typedef long double lb;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const ll N=2e5+10;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
struct Node{
ll l,r;
ll tag;
ll Max;
ll id;
}X[N<<2];
ll n,k,cnt,ans;
ll a[N],Ans[N],l[N],r[N],d[N],id[N],fa[N];
vector<pi> E[N];
map<pi,ll> F;
bool f[N];
void add(ll u,ll v,ll w){
E[u].push_back({v,w});
E[v].push_back({u,w});
}
void dfs(ll u,ll f){
l[u]=n,r[u]=1;
bool F=1;
for(auto t:E[u]){
ll v=t.fi,w=t.se;
if(v==f)
continue;
fa[v]=u;
d[v]=d[u]+w;
dfs(v,u);
l[u]=min(l[u],l[v]);
r[u]=max(r[u],r[v]);
F=0;
}
if(F){
id[++cnt]=u;
l[u]=r[u]=cnt;
a[cnt]=d[u];
}
}
void pushup(ll k){
X[k].Max=max(X[k<<1].Max,X[k<<1|1].Max);
if(X[k].Max==X[k<<1].Max)
X[k].id=X[k<<1].id;
else
X[k].id=X[k<<1|1].id;
}
void update(ll k,ll v){
X[k].Max+=v;
X[k].tag+=v;
}
void push_down(ll k){
if(X[k].tag){
update(k<<1,X[k].tag);
update(k<<1|1,X[k].tag);
X[k].tag=0;
}
}
void build(ll k,ll l,ll r){
X[k].l=l,X[k].r=r;
if(l==r){
X[k].Max=a[l];
X[k].id=l;
return ;
}
ll mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
void update(ll k,ll l,ll r,ll v){
if(X[k].l==l&&r==X[k].r){
update(k,v);
return ;
}
push_down(k);
ll mid=(X[k].l+X[k].r)>>1;
if(r<=mid)
update(k<<1,l,r,v);
else if(l>mid)
update(k<<1|1,l,r,v);
else{
update(k<<1,l,mid,v);
update(k<<1|1,mid+1,r,v);
}
pushup(k);
}
void solve(ll u){
while(!f[u]&&fa[u]){
f[u]=1;
update(1,l[u],r[u],-F[{u,fa[u]}]);
u=fa[u];
}
}
int main(){
n=read();
for(int u,v,w,i=1;i<n;i++){
u=read(),v=read(),w=read();
F[{u,v}]=F[{v,u}]=w;
add(u,v,w);
}
dfs(1,0);
build(1,1,cnt);
For(i,1,n){
if(X[1].Max>0)
ans+=X[1].Max;
if(X[1].Max>0)
solve(id[X[1].id]);
write(ans*2);
putchar('\n');
}
return 0;
}