D2
T1
树状数组好题
妙处:
- 三维偏序根据一个来排序,一个做下标,一个插入值,降成了二维
- 离散化的妙处:把b的前缀和进行离散化处理,很方便的作为下标插入了树状数组
思路:
- 将 a [ ] , b [ ] a[],b[] a[],b[]预处理成 ∑ a i , ∑ b i \sum{a_i},\sum{b_i} ∑ai,∑bi,按照现在的a数组升序排序
- 离散化 b b b数组,在树状数组中查询比 b b b数组代表的数的当前 i d id id小的数中 i d id id最小的是哪个
- 将当前的数插入树状数组
#include <bits/stdc++.h>
using namespace std;
const int N=1000050;
inline long long read(){
long long cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c&15);c=getchar();}
return cnt*f;
}
const int inf=1e9;
typedef long long ll;
ll c[N];int siz,n;
struct node{ll a,b;int id;}x[N];
bool cmp(node A,node B){return A.a<B.a;}
struct BIT{
int c[N];BIT(){memset(c,0x3f,sizeof(c));}
void add(int x,int v){for(;x<=siz;x+=x&(-x))c[x]=min(c[x],v);}
int ask(int x){int ans=inf;for(;x;x-=x&(-x))ans=min(ans,c[x]);return ans;}
}bit;
int main(){
n=read();
for(int i=1;i<=n;i++){x[i].a=x[i-1].a+read();x[i].id=i;}
for(int i=1;i<=n;i++){x[i].b=read()+x[i-1].b;c[++siz]=x[i].b;}
sort(x,x+1+n,cmp);sort(c+1,c+1+siz);siz=unique(c+1,c+1+n)-(c+1);int ans=0;
for(int i=0;i<=n;i++){
int p=lower_bound(c+1,c+siz+1,x[i].b)-c;
ans=max(ans,x[i].id-bit.ask(p));
bit.add(p,x[i].id);
}
printf("%d",ans); return 0;
}
T2
思路:可以看到,当前的权值和是由一棵棵子树的权值和构成的,子树上的节点又是一段段连续的区间,于是我们就想到了区间DP。可以用四边形不等式证明此时决策是单调的这位blog写的太好了orz。
设
f
i
,
j
f_{i,j}
fi,j表示只考虑从节点
i
i
i到
j
j
j时我们得到的最优答案,则有:
f
i
,
j
=
m
i
n
{
f
i
,
k
−
1
+
f
k
+
1
,
j
}
f_{i,j}=min\{f_{i,k-1}+f_{k+1,j}\}
fi,j=min{fi,k−1+fk+1,j}+
x
[
i
x[i
x[i
t
o
to
to
j
]
j]
j]
又因为我们已经证明了决策点是单调的,所以只需要在
[
i
−
1
,
j
]
[i-1,j]
[i−1,j]与
[
i
,
j
+
1
]
[i,j+1]
[i,j+1]中枚举决策点即可,然后就把时间复杂度从
O
(
n
3
)
O(n^3)
O(n3)优化到了
O
(
n
2
)
O(n^2)
O(n2)。
#include <cstdio>
#include <string>
using namespace std;
const int N=5050;
int g[N][N];
long long f[N][N],x[N],n;
inline long long read(){
long long cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c&15);c=getchar();}
//上一段读入优化要把(c^48)改成(c&15)时间才卡的过去
return cnt*f;
}
int main(){
n=read();
for(int i=1;i<=n;i++)
x[i]=read(),x[i]+=x[i-1];
for(int i=1;i<=n;i++)
f[i][i]=x[i]-x[i-1],g[i][i]=i;
register int j,i,l,k;
for(k=1;k<n;k++){
for(i=1;(j=i+k)<=n;i++){
f[i][j]=1e18;
for(l=g[i][j-1];l<=g[i+1][j];l++){
if(f[i][l-1]+f[l+1][j]<f[i][j])
f[i][j]=f[i][l-1]+f[l+1][j],g[i][j]=l;
}
f[i][j]+=x[j]-x[i-1];
}
}
printf("%lld",f[1][n]);
return 0;
}
T3
w a l k walk walk ( f r o m (from (from s o l u t i o n ) solution) solution):
我们首先考虑单个的 k k k。设 f i f_i fi表示从 i i i出发第一次到 k k k的期望步数,那么 f k = 0 , f i = ∑ ( f j ) x i + 1 f_k=0,f_i=\frac{\sum(f_j)}{x_i}+1 fk=0,fi=xi∑(fj)+1 ( i ! = k i!=k i!=k, j j j为 i i i的所有出边, x i x_i xi为 i i i的出度),用高斯消元解出 f 1 f_1 f1即可。
我们将每个点的方程先按第二种方式写出,那么对于每一个 k k k,我们只需要修改矩阵的一行。使用分治消元即可求出答案。
分治消元的具体做法是:先用前一半的方程进行消元,然后递归后一半;(恢复原矩阵后)再用后一半的方程进行消元,然后递归前一半。这样当区间缩小到单点时,这个方程并没有拿来消其它的方程,我们可以直接修改它并求出所有 f i f_i fi。 时间复杂度 O ( n 3 ) O(n^3) O(n3)
注:代码来自 s t d std std,非本人
#include <cstdio>
#include<iostream>
#define L long long
#define vi vector<int>
#define pb push_back
using namespace std;
const int q=998244353;
int n,m,f[310][310],g[10][310][310],x[310][310];
bool y[310][310];
inline int power(int a,int b)
{
if(!b)
return 1;
int c=power(a,b>>1);
c=(L)c*c%q;
if(b&1)
c=(L)c*a%q;
return c;
}
inline void dfs(int l,int i)
{
int j;
x[l][i]=f[i][0];
for(j=1;j<=n;j++)
if(j!=i && f[i][j])
{
if(!y[l][j])
dfs(l,j);
x[l][i]=(x[l][i]-(L)f[i][j]*x[l][j])%q;
}
y[l][i]=1;
}
inline void calc(int l,int r,int p)
{
int i,j,k,m=(l+r)>>1;
if(l==r)
{
y[l][l]=1;
for(i=1;i<=n;i++)
if(!y[l][i])
dfs(l,i);
return;
}
for(i=l;i<=r;i++)
{
g[p][i][0]=f[i][0];
for(j=l;j<=r;j++)
g[p][i][j]=f[i][j];
}
for(i=l;i<=m;i++)
{
k=power(f[i][i],q-2);
f[i][0]=(L)f[i][0]*k%q;
for(j=i;j<=r;j++)
f[i][j]=(L)f[i][j]*k%q;
for(j=i+1;j<=r;j++)
if(f[j][i])
{
f[j][0]=(f[j][0]-(L)f[i][0]*f[j][i])%q;
for(k=r;k>=i;k--)
f[j][k]=(f[j][k]-(L)f[i][k]*f[j][i])%q;
}
}
calc(m+1,r,p+1);
for(i=l;i<=r;i++)
{
f[i][0]=g[p][i][0];
for(j=l;j<=r;j++)
f[i][j]=g[p][i][j];
}
for(i=r;i>m;i--)
{
k=power(f[i][i],q-2);
f[i][0]=(L)f[i][0]*k%q;
for(j=l;j<=i;j++)
f[i][j]=(L)f[i][j]*k%q;
for(j=i-1;j>=l;j--)
if(f[j][i])
{
f[j][0]=(f[j][0]-(L)f[i][0]*f[j][i])%q;
for(k=l;k<=i;k++)
f[j][k]=(f[j][k]-(L)f[i][k]*f[j][i])%q;
}
}
calc(l,m,p+1);
for(i=l;i<=r;i++)
{
f[i][0]=g[p][i][0];
for(j=l;j<=r;j++)
f[i][j]=g[p][i][j];
}
}
int main()
{
int i,j,k;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
{
scanf("%d%d",&j,&k);
f[j][j]++;
f[j][0]++;
f[j][k]--;
}
calc(1,n,0);
for(i=2;i<=n;i++)
printf("%d\n",(x[i][1]+q)%q);
return 0;
}