4.9省选模拟
\(T1\)
不会建图,一开始一直认为\(k\)流量,每一单位流量可以看做是一个位置,然后并不会处理每个点必须被经过的问题
考虑正解做法
较为显然,第\(i\)和第\(j\)天都出现同一本书,并且中间没有扔掉,那么我们只需要花费第一本代价
如果我选择在\(i\sim j-1\)扔掉这本书,那么都是要扔掉,那么提前扔可以给后面更多余地,那么我们一开始并不知道怎么选最优,就最劣的全部每次重新买,再考虑把花费多的给减去,我们假如一本书从\(i\)购入一直留到\(j\)还没有卖,相当于在最劣的费用上减去了\(val,\)那么就可以理解为在上一个时刻花费了\(-val\)的代价
那么建图
拆点,每天有两个节点\(v_1,v_2\)分别是入点和出点
那么入点都往下连边表示书架上留下的书,上个时间往上次出现连边,表示卖掉,每个出点有两个决策,扔或卖
#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define int long long
#define MAXN 100005
using namespace std;
int head[MAXN],cost[MAXN],nxt[MAXN],val[MAXN],to[MAXN],tot=1;
int need[MAXN],dis[MAXN],pre[MAXN],fy[MAXN],Ans,n,k,s,t;
bool in[MAXN],vis[MAXN];
void add(int u,int v,int w,int cs)
{
tot++;to[tot]=v;val[tot]=w;cost[tot]=cs;nxt[tot]=head[u];head[u]=tot;
swap(u,v);w=0,cs*=-1;
tot++;to[tot]=v;val[tot]=w;cost[tot]=cs;nxt[tot]=head[u];head[u]=tot;
}
bool spfa(int s,int t)
{
queue<int>q;
memset(dis,0x3f,sizeof(dis));
q.push(s);
dis[s]=0;
in[s]=true;
while(!q.empty())
{
int now=q.front();
q.pop();
in[now]=false;
for(int i=head[now];i!=-1;i=nxt[i])
{
int y=to[i];
if(val[i]&&dis[y]>dis[now]+cost[i])
{
dis[y]=dis[now]+cost[i];
if(!in[y]) in[y]=true,q.push(y);
}
}
}
return dis[t]!=INF;
}
int dfs(int now,int ed,int flow)
{
if(now==ed) return flow;
vis[now]=true;
int rest=flow;
for(int i=head[now];i!=-1&&rest;i=nxt[i])
{
int y=to[i];
if(vis[y]||dis[y]!=dis[now]+cost[i]||!val[i]) continue;
int k=dfs(y,ed,min(rest,val[i]));
rest-=k;
val[i]-=k;
val[i^1]+=k;
if(!k) dis[y]=INF;
}
vis[now]=false;
return flow-rest;
}
signed main()
{
scanf("%lld%lld",&n,&k);
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++)
{
scanf("%lld",&need[i]);
}
for(int i=1;i<=n;i++)
{
fy[i]=1;
}
t=n*2+1;
for(int i=1;i<=n;i++)
{
add(i,i+n,1,0);
add(i+n,t,1,0);
}
for(int i=1;i<=n;i++)
{
if(i>1) add(i-1,i,k-1,0);
add(s,i,1,fy[need[i]]);
if(pre[need[i]])
{
add(i-1,pre[need[i]]+n,1,-fy[need[i]]);
}
pre[need[i]]=i;
}
while(spfa(s,t))
{
Ans+=dfs(s,t,INF)*dis[t];
}
printf("%lld\n",Ans);
}
\(T2\)
\(emm,\)一眼看上去是数据结构(当然我的一眼肯定对不了)
其实是个分块/李超树+凸包斜率?
树上转线性,考虑在\(dfs\)序列上搞事情就好了
那么就变成了,区间\(suma+,\)找区间最大值
这个东西由于有第二个参数,肯定不能直接维护,那么就感觉,这个东西很像能函数维护的样子
我们每个点的\(b\)不变,那么怎么处理\(a\)的变化\(?\)
首先式子可以写成\(y=b_ix+a_i\times b_i\)的形式
\(x\)就是\(add\),我们需要找到每段区间最大的\(y,\)那么直接维护一个凸包,找最大值就好了,然后修改完之后打上标记,下次询问时候重构就好了
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define MAXN 200020
int n,Q,f[MAXN];
int num,dfn[MAXN],siz[MAXN],id[MAXN];
vector<int> G[N];
ll a[N],b[N];
void dfs(int u)
{
dfn[u]=++num;
id[num]=u;
siz[u]=1;
for(auto v:G[u])
{
b[v]+=b[u];
a[v]+=a[u];
dfs(v);
siz[u]+=siz[v];
}
}
struct Line
{
ll k,b;
Line(ll _k=0,ll _b=0){k=_k,b=_b;}
};
class Segment_Tree
{
struct node
{
Line p;
ll t,add;
node(){t=add=0;}
inline void Add(int d){
t-=d,add+=d;
p.b+=p.k*d;
}
}tree[N<<2];
#define ls u<<1
#define rs u<<1|1
inline void update(int u)
{
tree[u].t=min(tree[ls].t,tree[rs].t);
Line x=tree[ls].p,y=tree[rs].p;
if(x.b<y.b||(x.b==y.b&&x.k<y.k))swap(x,y);
tree[u].p=x;
if(x.k<y.k)tree[u].t=min(tree[u].t,(x.b-y.b+y.k-x.k-1)/(y.k-x.k));
}
inline void pushdown(int u)
{
if(tree[u].add)
{
tree[ls].Add(tree[u].add);
tree[rs].Add(tree[u].add);
tree[u].add=0;
}
}
public:
void build(int u,int L,int R,int op)
{
if(L==R)
{
tree[u].p=Line(b[id[L]]*op,1LL*a[id[L]]*b[id[L]]*op);
tree[u].t=inf;
return;
}
int mid=(L+R)>>1;
build(ls,L,mid,op),build(rs,mid+1,R,op);
update(u);
}
void change(int u,int L,int R,int l,int r,int d)
{
if(L>=l&&R<=r&&tree[u].t>d)
{
tree[u].Add(d);
return;
}
pushdown(u);
int mid=(L+R)>>1;
if(l<=mid)change(ls,L,mid,l,r,d);
if(r>mid)change(rs,mid+1,R,l,r,d);
update(u);
}
ll Query(int u,int L,int R,int l,int r)
{
if(L>=l&&R<=r){
return tree[u].p.b;
}
pushdown(u);
int mid=(L+R)>>1;
ll mx=-inf;
if(l<=mid)mx=max(mx,Query(ls,L,mid,l,r));
if(r>mid)mx=max(mx,Query(rs,mid+1,R,l,r));
return mx;
}
}T1,T2;
int main()
{
freopen("faraway.in","r",stdin);
freopen("faraway.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>Q;
for(int i=2;i<=n;++i)
{
cin>>f[i];
G[f[i]].push_back(i);
}
for(int i=1;i<=n;++i)cin>>a[i];
for(int i=1;i<=n;++i)cin>>b[i];
dfs(1);
T1.build(1,1,n,1);
T2.build(1,1,n,-1);
while(Q--)
{
int opt,u,x;
cin>>opt>>u;
if(opt==1)
{
cin>>x;
T1.change(1,1,n,dfn[u],dfn[u]+siz[u]-1,x);
T2.change(1,1,n,dfn[u],dfn[u]+siz[u]-1,x);
}
else
{
cout<<max(T1.Query(1,1,n,dfn[u],dfn[u]+siz[u]-1),T2.Query(1,1,n,dfn[u],dfn[u]+siz[u]-1))<<'\n';
}
}
return 0;
}
\(T3\)
模拟,%您