由于a大于0,所有二次函数均是开口向上,而且x必须正整数。所以很自然想到先全部分配1.
然后逐个分配,由于二次函数f[i]-f[i-1]左边一定比右边更优,即i越小结果越小。
所以我们可以直接把F[I]-F[I-1]丢到优先队列里。每次让某一个二次函数的x+1,根据前面的分析,一定是队列首的二次函数加1 更优,因为队列里的所有数,将来只可能更大,不会变小。
然后把F[I+1]-F[I]丢回去即可。
#include
using namespace std;
typedef long long ll;
const int M=1e5+7;
struct node
{
ll f,x,id;
bool operator <(const node &r)const
{
return f>r.f;
}
}p;
priority_queueq;//逆序规则,变成小根堆
int a[M],b[M],c[M];
ll s(int k,int x)
{
return 1LL*a[k]*x*x+1LL*b[k]*x+c[k];
}
int main()
{
int n,m;
cin>>n>>m;
ll ans=0;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i]>>c[i],ans+=s(i,1),q.push(node{s(i,2)-s(i,1),2,i});
m-=n;
while(m)
{
node tp=q.top();q.pop();
ans+=tp.f;
q.push(node{s(tp.id,tp.x+1)-s(tp.id,tp.x),tp.x+1,tp.id});
m--;
}
cout<
HDU - 6547
如果不在树上,这就是一道势能线段树的裸题。
在树上的话,树剖一下就行了。
两个知识点的混合板子题
#include
using namespace std;
#define ll long long
#define ls o*2
#define rs o*2+1
const int maxn=2e5+7;
ll w[maxn],wt[maxn];
struct node
{
int v,nxt;
}e[maxn];
int tot,head[maxn];
void add(int x,int y)
{
e[++tot].nxt=head[x];
e[tot].v=y;
head[x]=tot;
}
int son[maxn],id[maxn],fa[maxn],cnt,dep[maxn],siz[maxn],top[maxn];
int n,m,r,q;
void init()
{
tot=0,cnt=0;
}
ll tr[maxn<<2],tg[maxn<<2];
void bd(int o,int l,int r)
{
if(l==r)
{
tr[o]=wt[l];
if(tg[o]==1||tg[o]==0)tg[o]=1;
return;
}
int m=(l+r)/2;
bd(ls,l,m);
bd(rs,m+1,r);
tr[o]=tr[ls]+tr[rs];
}
void up(int o,int l,int r,int x,int y)
{
if(l==r)
{
tr[o]=sqrt(1.0*tr[o]);
if(tr[o]==1||tr[o]==0)
tg[o]=1;
return ;
}
int m=(l+r)/2;
if(x<=m)up(ls,l,m,x,y);
if(y>m)up(rs,m+1,r,x,y);
tr[o]=tr[ls]+tr[rs];
if(tg[ls]&&tg[rs])tg[o]=1;
}
ll qu(int o,int l,int r,int x,int y)
{
if(x<=l&&r<=y)
{
return tr[o];
}
int m=(l+r)/2;
ll ans=0;
if(x<=m)ans+=qu(ls,l,m,x,y);
if(y>m)ans+=qu(rs,m+1,r,x,y);
return ans;
}
//
void dfs1(int u,int f,int deep)
{
dep[u]=deep;
fa[u]=f;
siz[u]=1;
int maxson=-1;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].v;
if(v==f)continue;
dfs1(v,u,deep+1);
siz[u]+=siz[v];
if(siz[v]>maxson)
{
son[u]=v;
maxson=siz[v];
}
}
}
void dfs2(int u,int topf)
{
id[u]=++cnt;
wt[cnt]=w[u];
top[u]=topf;
if(!son[u])return ;
dfs2(son[u],topf);
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].v;
if(v==fa[u]||v==son[u])continue;
dfs2(v,v);
}
}
void updrange(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[x]dep[y])
swap(x,y);
up(1,1,n,id[x],id[y]);
}
ll qrange(int x,int y)
{
ll ans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]dep[y])
swap(x,y);
ans+=qu(1,1,n,id[x],id[y]);
return ans;
}
int main()
{
int u,v;
init();
cin>>n>>q;
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=1;i<=n-1;i++)
scanf("%d%d",&u,&v),add(u,v),add(v,u);
r=1;
dfs1(r,-1,1);
dfs2(r,r);
bd(1,1,n);
for(int i=1;i<=q;i++)
{
int opt,u,v,z;
scanf("%d%d%d",&opt,&u,&v);
if(opt==0)
{
updrange(u,v);
}
else{
printf("%lld\n",qrange(u,v));
}
}
}
HDU - 6549
f[i][j][k]表示: 处理到第i位,使当前位修改为 'a'+j ,且已经出现了k段的最小修改次数。
由于无后效性,我们直接线性DP从前往后推。
比较简单,难点是会自然想到26^2*10*n的做法。
我们让f[i][26][k]表示: 处理到第i位,使当前位修改为任意字符 ,且已经出现了k段的最小修改次数。
这样就能利用前面已经处理过的信息了 省去一维枚举字符。
转移比较简单,看看就差不多了
#include
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair pii;
typedef pair pll;
typedef pair pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_mapmp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)] //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 2e5+7;
/*int head[M],cnt;
struct EDGE{int to,nxt,val;}ee[M];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}*/
int f[M][27][11];
char s[M];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,l,z;
cin>>n>>l>>z;
cin>>s;
memset(f,127,sizeof(f));
for(int i=0;i<=25;i++)
f[0][i][1]=(s[0]=='a'+i?0:1);
f[0][26][1]=0;
for(int i=1;i0)
{
f[i][j][k]=min(f[i][j][k],f[i-l][26][k-1]+1);
f[i][j][k]=min(f[i][j][k],f[i-l][j][k]+1);
}
else
{
f[i][j][k]=min(f[i][j][k],f[0][26][k-1]+1);
f[i][j][k]=min(f[i][j][k],f[0][j][k]+1);
}
// cout<