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\)

模拟,%您

posted @ 2022-04-09 17:19  Authentic_k  阅读(36)  评论(0编辑  收藏  举报