动态规划
f [ i ] f[i] f[i]表示选择第i台发动机作为最后一台发动机,前i台发动机最大的总功率
f [ i ] f[i] f[i]可以从 f [ j ] f[j] f[j]转移,当且仅当 a [ j ] + x [ j ] < a [ i ] a[j]+x[j]<a[i] a[j]+x[j]<a[i]
对于每一个 i i i扫描小于 i i i的所有 j j j,转移DP。
后面几个点会超时。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100005;
int n;
ll a[N],p[N],to[N],f[N],ans;
ll read(){
ll sum=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
void print(ll x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)print(x/10);
putchar(x%10+'0');
}
int main(){
// freopen("earth.in","r",stdin);
// freopen("earth.out","w",stdout);
n=read();
int x;
for(int i=1;i<=n;i++)
{
a[i]=read();
p[i]=read();
x=read();to[i]=a[i]+x;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<i;j++)
if(to[j]<a[i])f[i]=max(f[j],f[i]);
f[i]+=p[i];
ans=max(ans,f[i]);
}
print(ans);
return 0;
}
和50pts思路一样,只不过从后向前扫描。
对于当前每一个 i i i,在 i + 1 i+1 i+1到n中用lower_bound查找最小的 j j j满足 a [ i ] + x [ i ] < a [ j ] a[i]+x[i]<a[j] a[i]+x[i]<a[j]
f [ i ] = m a x ( f [ i + 1 ] , f [ j ] + p [ i ] ) f[i]=max(f[i+1],f[j]+p[i]) f[i]=max(f[i+1],f[j]+p[i])
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100005;
int n;
ll a[N],p[N],to[N],f[N],ans;
ll read(){
ll sum=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
void print(ll x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)print(x/10);
putchar(x%10+'0');
}
int main(){
// freopen("earth.in","r",stdin);
// freopen("earth.out","w",stdout);
n=read();
int x;
for(int i=1;i<=n;i++)
{
a[i]=read();
p[i]=read();
x=read();to[i]=a[i]+x;
}
f[n]=p[n];
for(int i=n-1;i>=1;i--)
{
int j=upper_bound(a+1,a+1+n,to[i])-a;
f[i]=max(f[i+1],f[j]+p[i]);
}
print(f[1]);
return 0;
}
直接求a到b的最短路,统计并标记走过的路径。
再求c到d的最短路,对于未被标记的路径加入到答案中。
先求c到d,再求a到b也是80分
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3002,inf=0x3f3f3f3f;
int n,m,head[N],join[N][N],into[N],las[N],v[N],vis[N];
int tot=1,x,y,a,b,c,d,ans,cnt,res,cur;
struct edge{
int ver,to;
}e[N*2];
void add(int x,int y)
{
e[++tot].ver=y;
e[tot].to=head[x];
head[x]=tot;
}
int read(){
int sum=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
void print(int x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)print(x/10);
putchar(x%10+'0');
}
void dfs1(int a,int b,int now,int sum){
if(sum>=ans)return;
if(now==b){
cnt=0;ans=sum;
while(into[now])
{
las[++cnt]=into[now];
now=e[into[now]^1].ver;
}
return;
}
for(int i=head[now];i;i=e[i].to)
{
int y=e[i].ver;
if(vis[y])continue;
vis[y]=1;
into[y]=i;
dfs1(a,b,y,sum+1);
vis[y]=0;
}
}
void dfs2(int a,int b,int now,int sum){
if(sum>=ans)return;
if(now==b){ans=sum;return;}
for(int i=head[now];i;i=e[i].to)
{
int y=e[i].ver;
if(vis[y])continue;
vis[y]=1;
if(!v[i])dfs2(a,b,y,sum+1);
else dfs2(a,b,y,sum);
vis[y]=0;
}
}
void update(int x)
{
if(x==1)
for(int i=1;i<=cnt;i++)v[las[i]]=v[las[i]^1]=1;
else
for(int i=1;i<=cnt;i++)v[las[i]]=v[las[i]^1]=0;
}
int main(){
// freopen("graph.in","r",stdin);
// freopen("graph.out","w",stdout);
n=read();m=read();
a=read();b=read();c=read();d=read();
for(int i=1;i<=m;i++)
{
x=read();y=read();
if(join[x][y])continue;
add(x,y);add(y,x);
join[x][y]=join[y][x]=1;
}
ans=inf;
vis[a]=1;dfs1(a,b,a,0);vis[a]=0;
cur=ans;update(1);
ans=inf;
vis[c]=1;dfs2(c,d,c,0);vis[c]=0;
res=cur+ans;update(0);
print(res);
return 0;
}
分两种情况:
(1):a-b,c-d 的路径不相交,则答案一定是 a-b,c-d 的最短路之和。
(2):a-b,c-d 的路径相交,则相交部分一定是连续的一段路径,
于是 O ( n 2 ) O(n^2) O(n2)预处理每对点的最短路,再 O ( n 2 ) O(n^2) O(n2)枚举相交的路径的两端,算一下总长就好了。
以上两种情况所有方案取最小值即可。
时间复杂度 O ( n 2 O(n^2 O(n2
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3002,inf=0x3f3f3f3f;
int n,m,a,b,c,d,d1[N],d2[N],d3[N],d4[N],dd[N];
int join[N][N],v[N],tot,head[N],res;
queue<int>q;
struct edge{
int ver,to;
}e[N*2];
void add(int x,int y)
{
e[++tot].ver=y;
e[tot].to=head[x];
head[x]=tot;
}
int read(){
int sum=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
void print(int x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)print(x/10);
putchar(x%10+'0');
}
void spfa(int x,int *d)
{
memset(d,inf,sizeof(d));
memset(v,0,sizeof(v));
q.push(x);
d[x]=0;
v[x]=1;
while(q.size()){
int x=q.front();
q.pop();
for(int i=head[x];i;i=e[i].to)
{
int y=e[i].ver;
if(v[y])continue;
d[y]=d[x]+1;v[y]=1;q.push(y);
}
}
}
int main(){
// freopen("graph.in","r",stdin);
// freopen("graph.out","w",stdout);
n=read();m=read();
a=read();b=read();c=read();d=read();
int x,y;
for(int i=1;i<=m;i++)
{
x=read();y=read();
add(x,y);add(y,x);
}
spfa(a,d1); spfa(b,d2);spfa(c,d3);spfa(d,d4);
res=d1[b]+d3[d];
for(int i=1;i<=n;i++)
{
spfa(i,dd);
for(int j=i;j<=n;j++)
{
res=min(res,min(d1[i]+d3[i]+d2[j]+d4[j],d1[j]+d3[j]+d2[i]+d4[i])+dd[j]);
res=min(res,min(d1[i]+d4[i]+d2[j]+d3[j],d1[j]+d4[j]+d2[i]+d3[i])+dd[j]);
}
}
print(res);
return 0;
}
首先每一个二进制位可以拆开来看,对答案没有影响。
树形DP,对每个二进制位分别统计对答案的影响。记 f [ x ] [ i ] [ j ] [ 0 / 1 ] f [x][i][j][0/1] f[x][i][j][0/1]为当前以x为根的子树中,深度为i的所有节点的第j位是 0 / 1 0/1 0/1的数量。每将一个儿子y合并上去时,先利用前缀和统计两点分别在当前以x为根的子树和以y为根的子树中的答案,再将y的信息合并进x中。
还要用点分治优化,每次统计答案时都只会用线段树统计后缀和,而且发现第二维状态大小只和当前子树深度有关,于是使用长链剖分,统计后缀和的方法合并子树信息和统计答案。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500005;
int size,firs[N],n,k,dep[N],son[N],cnt=1;
struct node{int f[20][2];}b[N],a[N],sa,tb;
node *f[N];
long long ans;
struct edge{int u,ver,to;}e[N*2];
ll read(){
ll sum=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
return sum*f;
}
node operator+(node a,int b){
for (int i=0;i<20;i++){a.f[i][b&1]++;b>>=1;}
return a;
}
void operator+=(node &a,node b){
for (int i=0;i<20;i++){a.f[i][0]+=b.f[i][0];a.f[i][1]+=b.f[i][1];}
}
node operator+(node a,node b){a+=b;return a;}
node operator-(node a,node b){
for(int i=0;i<20;i++){a.f[i][0]-=b.f[i][0];a.f[i][1]-=b.f[i][1];}
return a;
}
ll operator*(node &a,node &b){
ll ans=0;
for (int i=0;i<20;i++)ans+=((ll)a.f[i][0]*b.f[i][1]+(ll)a.f[i][1]*b.f[i][0])<<i;
return ans;
}
void add(int u,int ver){e[++size]=(edge){u,ver,firs[u]};firs[u]=size;}
void dfs(int x,int fa){
dep[x]=1;
for (int i=firs[x];i;i=e[i].to)
if (e[i].ver!=fa){
int y=e[i].ver;dfs(y,x);
if (dep[y]+1>dep[x]){
dep[x]=dep[y]+1;
son[x]=y;
}
}
}
node calc(int x,int i){
if (i<0) return f[x][0];
return (dep[x]<=i) ? b[0] : f[x][i];
}
node& calc(int x,int l,int r,node &a){return a=calc(x,l)-calc(x,r+1);}
void merge(int x,int y){
for (int i=0;i<dep[y];i++)
ans+=calc(y,i,i,sa)*calc(x,0,k-i-1,tb);
for (int i=0;i<dep[y];i++)
f[x][i+1]+=f[y][i];
f[x][0]+=f[y][0];
}
void update(int x){f[x]=b+cnt;cnt+=dep[x];}
void dp(int x,int fa){
if (son[x]){
f[son[x]]=f[x]+1;
dp(son[x],x);
f[x][0]=f[x][1]+a[x];
ans+=a[x]*calc(x,0,k,sa);
}
else f[x][0]=a[x];
for (int i=firs[x];i;i=e[i].to)
if ((e[i].ver!=fa)&&(e[i].ver!=son[x])){
int y=e[i].ver;update(y);dp(y,x);
merge(x,y);
}
}
int main(){
// freopen("tree.in","r",stdin);
// freopen("tree.out","w",stdout);
n=read();k=read();
for (int i=1;i<=n;i++){
int x=read();a[i]=a[i]+x;
}
for (int i=1;i<n;i++){
int u=read(),ver=read();add(u,ver);add(ver,u);
}
dfs(1,0);update(1);dp(1,0);
printf("%lld\n",ans);
return 0;
}