牛客练习赛40

A.dp水题,不多讲

B.

首先我们显然贪心一下,及操作顺序按时间从小到大排序一下,那么我们直接用个priority维护一下z的最大值,因为如果时间超出了限制,那么我们一定是贪心选从前z最大的来花金币

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
int n;
struct node{
    int x,y,z;
}v[MAXN];
double ans;
priority_queue<pair<int,int> >q;
bool cmp(node x,node y)
{
    return x.y<y.y;
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%d%d%d",&v[i].z,&v[i].x,&v[i].y);
    sort(v+1,v+n+1,cmp);
    int  time=0;
    for (int i=1;i<=n;i++)
    {
       time+=v[i].x;
        q.push(make_pair(v[i].z,v[i].x));
        while (time>v[i].y)
        {
            pair<int,int> x=q.top(); q.pop();
            if (time-x.second==v[i].y)
            {
                time=v[i].y; ans+=x.second*1.0/(1.0*x.first);  break;
            }
            else if (time-x.second<v[i].y)
            {
                ans+=(time-v[i].y)*1.0/(1.0*x.first);
                x.second-=(time-v[i].y); time=v[i].y;
                q.push(x); break;
            } else {
                time-=x.second; ans+=x.second*1.0/(1.0*x.first);
            }
        }
    }
    printf("%.1lf\n",ans);
}


C.就是所有边权和×2减直径

D.这是神仙qy教我的,膜一发.

我们考虑一下右端以r为结尾的最大值

\\f(i)=\sum_{j=1}^{i}a(j) \\s(i)=\sum_{j=1}^{i}j*a(j) \\ ansr=max(s(r)-s(l)-l*(f(r)-f(l)),l\epsilon (0,1,2...r-1))

我们推一发式子就得到了

l*f(l)-s(l)=l*f(r)+ansr-s(r)

我们发现这就可以看成y=kx+b,此时就是求截距最大值

这就可以用斜率优化了

但是实际上这个斜率即f(r)并不是单调的,所以我们在维护下凸壳的时候并不能把队首给删掉,因为斜率的不确定性,需要二分寻找斜率(其实思考斜率优化本质显然)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=2e5+10;
struct node {
    ll x,y;
}q[MAXN];
int n;
ll s[MAXN],a[MAXN],f[MAXN],sum[MAXN],ans;
int len;
bool pan1(int x,int y,ll z)
{
    return (q[y].y-q[x].y)>=z*(q[y].x-q[x].x);
}
ll findans(int l,int r,ll x)
{
    if (l==r) return l;
    int mid=(l+r)/2;
    if (pan1(mid,mid+1,x))
    {
        return findans(mid+1,r,x);
    } else return findans(l,mid,x);
}
bool pan(int x,int y,int z)
{
    return (q[x].y-q[y].y)*(q[y].x-q[z].x)>=(q[y].y-q[z].y)*(q[x].x-q[y].x);
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        f[i]=(f[i-1]+a[i]);
        s[i]=(s[i-1]+(ll)i*a[i]);
        sum[i]=((ll)i*f[i]-s[i]);
    }
    len=1;
    q[len].x=0;
    q[len].y=0;
    ans=-100000000000000000;
    for (int i=1;i<=n;i++)
    {
        int t=findans(1,len,f[i]); 
        ans=max(ans,q[t].y-q[t].x*f[i]+s[i]);
        q[++len].x=i; q[len].y=sum[i];
        if (len>=3)
        while (pan(len,len-1,len-2))
        {
            len--; q[len].x=q[len+1].x; q[len].y=q[len+1].y;
            if (len<3) break;
        }
    }
    printf("%lld\n",ans);
}

 

E.

又是一道莫比乌斯反演题..

让你求:

\prod_{i=1}^{n}\prod_{j=1}^{m}g(gcd(i,j))

其中g函数的定义如下:

x可以被分解成若干质数的乘积

x=\prod_{i=1}^{q}p_{i}^_{k_{i}}

那么

g(x)=\left\{\begin{matrix} 1 & & x=1\\ \sum_{i=1}^{q}k_{i}& & otherwise \end{matrix}\right.

我们冷静分析一下,因为他所要求的是乘积,那么我们就应该知道利用连续一段的gcd为x的出现个数是相同的,这样有根号段

所以我们首先可以预处理出g函数这个东西直接欧筛就行了

然后我们思考一下反演

\prod_{T=1}^{min(n,m)}{g(T)^_{\sum_{i=1}^{n/T}\sum_{j=1}^{m/T}gcd(i,j)==1}}

s(T)=={\sum_{d|T}g(d)^{\mu (T/d)}

则上式为\prod_{i=1}^{min(n,m)}s(T)^_{[n/T]*[m/T]}

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
const int N=250000;
ll sum[N+10],mu[N+10],ans[N+10],inv[N+10];
int len;
bool p[N+10];
int prime[N+10];
int T;
ll n,m;
ll ksm(ll x,ll y)
{
	ll ans=1;
	for (;y;y>>=1,x=(x*x)%mod) if (y&1) ans=(ans*x)%mod;
	return ans;
}
ll pan(ll x,ll y)
{
	if (y==1) return x;
	else if (y==-1) return ksm(x,mod-2); else return 1;
}
void init()
{
	sum[1]=1; mu[1]=1;
	for (int i=2;i<=N;i++)
	{
		if (!p[i])
		{
			sum[i]=1; prime[++len]=i; mu[i]=-1;
		}
		for (int j=1;prime[j]*i<=N;j++)
			{
				p[prime[j]*i]=1;
				sum[prime[j]*i]=sum[i]+1;
				if (i%prime[j]==0) { mu[i*prime[j]]=0; break;}
				else mu[i*prime[j]]=-mu[i];
			}
	}
	for (int i=1;i<=N;i++) ans[i]=1;
	for (int i=1;i<=N;i++)
	{
		for (int j=1;j*i<=N;j++)
			ans[i*j]=(ans[i*j]*pan(sum[i],mu[j]))%mod;
	}
	inv[0]=1;
	for (int i=1;i<=N;i++) inv[i]=ksm(ans[i],mod-2);
	for (int i=2;i<=N;i++) ans[i]=(ans[i-1]*ans[i])%mod;
    for (int i=2;i<=N;i++) inv[i]=(inv[i]*inv[i-1])%mod;
}
int main()
{
	scanf("%d",&T);
	init();
	while (T--)
	{
		scanf("%lld%lld",&n,&m);
		ll k=0,s=1;
		for (ll i=1;i<=n&&i<=m;i=k+1)
		{ 
            k=min(n/(n/i),m/(m/i));
            s=(s*ksm((ans[k]*inv[i-1]%mod)%mod,(n/i)*(m/i)))%mod;
		}
		printf("%lld\n",s);
	}
}

F.

我们考虑一下这一定是网络流

所以我们简化一下模型即是一对点的关系边

再加上我们分类讨论一下最小割的情况,最后人工手动平衡点权即可,就可以得到答案

具体细节不讲了

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+10;
const int MAXN=1e3+10;
struct node{
    int u,to,w;
}edge[MAX];
int head[MAXN],arrange[MAXN],w[MAXN],cur[MAXN],l[MAXN];
int x,y,n,v0,v1,v2,ans,s,t,q,k;
void add(int x,int y,int z)
{
    edge[k].u=y;
    edge[k].to=head[x];
    edge[k].w=z;
    head[x]=k++;
}
bool bfs(){
    memset(arrange,0,sizeof(arrange));
    arrange[s]=1;
    queue<int>q;
    q.push(s);
    while (!q.empty())
    {
         int p=q.front(); q.pop();
         if (t==p) return 1;
         for (int i=head[p];i!=-1;i=edge[i].to)
         {
            int u=edge[i].u;
            if (!arrange[u]&&edge[i].w)
            {
                arrange[u]=arrange[p]+1;
                q.push(u);
            }
         }
    }
    return 0;
}
int dfs(int now,int maxlow)
{
    if (now==t) return maxlow;
    int ret=0;
    for (int &i=cur[now];i!=-1;i=edge[i].to)
    {
        int u=edge[i].u;
        if (edge[i].w&&arrange[u]==arrange[now]+1)
        {
            int f=dfs(u,min(maxlow-ret,edge[i].w));
            edge[i].w-=f; edge[i^1].w+=f;
            ret+=f;
            if (maxlow==ret) return ret;
        }
    }
    return ret;
}
int dinic()
{
    int ans=0;
    while (bfs())
    {
        memcpy(cur,head,sizeof(head));
        ans+=dfs(s,0x3f3f3f3f);
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&q);
    memset(head,-1,sizeof(head));
    for (int i=1;i<=n;i++) scanf("%d",&w[i]);
    for (int i=1;i<=n;i++) ans+=w[i];
    s=n+1,t=n+2;
    for (int i=1;i<=q;i++)
    {
        scanf("%d%d%d%d%d",&x,&y,&v0,&v1,&v2);
        ans+=v0+v1;
        add(x,y,(v0+v1)/2+v2); add(y,x,0);
        add(y,x,(v0+v1)/2+v2); add(x,y,0);
        w[x]+=v0/2; w[y]+=v0/2;
        l[x]+=v1/2; l[y]+=v1/2;
    }
    for (int i=1;i<=n;i++) 
    {
        add(s,i,w[i]); add(i,s,0);
        add(i,t,l[i]); add(t,i,0);
    }
    printf("%d\n",ans-dinic());
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值