zoukankan      html  css  js  c++  java
  • [CSP-S模拟测试92]题解

    A.数列

    显然每个数的答案是互相独立的,直接扩欧求解。我们需要最小化$ax+by=gcd(a,b)$中的$|x|+|y|$,而显然当x或y靠近0时答案可能最优,列个不等式求一下即可。

    能$O(1)$千万不要懒。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    using namespace std;
    const int N=1e5+5;
    int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    	return x*f;
    }
    typedef long long ll;
    int n;
    ll s[N];
    ll x,y,A,B;
    ll gcd(ll a,ll b)
    {
    	if(!b)return a;
    	return gcd(b,a%b);
    }
    ll abss(ll x)
    {
    	return x>0?x:-x;
    }
    void exgcd(ll a,ll b,ll &x,ll &y)
    {
    	if(b==0)
    	{
    		x=1;y=0;return ;
    	}
    	exgcd(b,a%b,x,y);
    	ll t=x;x=y;y=t-a/b*y;
    }
    void test()
    {
    	A=read();B=read();int G=gcd(A,B);
    	cout<<G<<endl;
    	exgcd(A,B,x,y);
    	cout<<x<<' '<<y<<endl;
    }
    int main()
    {
    	//freopen("array.in","r",stdin);
    	n=read();A=read();B=read();
    	for(int i=1;i<=n;i++)
    		s[i]=read();
    	ll ans=0;ll G=gcd(A,B),ta=A/G,tb=B/G;
    	exgcd(A/G,B/G,x,y);
    
    	for(int i=1;i<=n;i++)
    	{
    		if(s[i]%G!=0)
    		{
    			puts("-1");
    			exit(0);
    		}
    		ll res=1e15;
    
    		ll t=s[i]/G;
    		ll nowx=x*t,nowy=y*t;
    		res=min(res,abss(nowx)+abss(nowy));
    		ll p=-nowx/tb;
    		res=min(res,abss(nowx+p*tb)+abss(nowy-p*ta));
    		res=min(res,abss(nowx+(p-1)*tb)+abss(nowy-(p-1)*ta));
    		res=min(res,abss(nowx+(p+1)*tb)+abss(nowy-(p+1)*ta));
    		p=-nowy/ta;
    		res=min(res,abss(nowx-p*tb)+abss(nowy+p*ta));
    		res=min(res,abss(nowx-(p-1)*tb)+abss(nowy+(p-1)*ta));
    		res=min(res,abss(nowx-(p+1)*tb)+abss(nowy+(p+1)*ta));
    
    		//cout<<nowx<<' '<<nowy<<endl;
    		ans+=res;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    B.数对

    可任意排序看似难以解决,但考虑一下$a$和$b$之间的限制,不难发现如果$a_i<b_j and b_i<a_j$,$i$应当尽可能在$j$前面,如果恰好相反那么$i$就应该在后面,至于剩下的情况,怎么排都是一样的。那么按$a+b$排序就能保证选取一定最优。

    之后就是原题了。线段树维护dp板子。

    (忽然发现我好像没写过之前那题的题解 懒癌发作失败)

    那么现在问题就转化成了给你一个二元组序列,要求你按顺序选几个二元组,在满足所有$a_i leq b_j (i<j)$的情况下最大化权值。

    设$dp[i][j]$表示当前考虑到i并且选了它,且所选集合中最大的$a[]$值为j的最大收益。($a[]$和$b[]$当然是要离散化的= =)

    暴力$O(n^3)$转移就很显然了:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    	return x*f;
    }
    const int N=1e5+5;
    typedef long long ll;
    int n,a[N],b[N],S;
    
    ll w[N],dp[2505][2505],ans;
    vector<int> c;
    struct node
    {
    	int a,b;ll w;
    }s[N];
    bool cmp(node x,node y)
    {
    	if(max(x.b,x.a)!=max(y.b,y.a))
    		return max(x.b,x.a)<max(y.b,y.a);
    	else return x.b<y.a;
    }
    int main()
    {
    	//freopen("pair.in","r",stdin);
    	n=read();
    	for(int i=1;i<=n;i++)
    		s[i].a=read(),s[i].b=read(),s[i].w=read();
    	sort(s+1,s+n+1,cmp);
    	for(int i=1;i<=n;i++)
    		a[i]=s[i].a,b[i]=s[i].b,c.push_back(a[i]),c.push_back(b[i]),w[i]=s[i].w;
    	sort(c.begin(),c.end());vector<int>::iterator it=unique(c.begin(),c.end());
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=lower_bound(c.begin(),it,a[i])-c.begin()+1;
    		b[i]=lower_bound(c.begin(),it,b[i])-c.begin()+1;
    		S=max(S,max(a[i],b[i]));
    	}
    	for(int i=1;i<=n;i++)
    	{
    		dp[i][a[i]]=w[i];
    		for(int j=1;j<i;j++)
    			for(int k=1;k<=b[i];k++)
    				dp[i][max(k,a[i])]=max(dp[i][max(k,a[i])],dp[j][k]+w[i]),ans=max(ans,dp[i][max(a[i],k)]);
    	}
    
    	cout<<ans<<endl;
    	return 0;
    }
    

    然后你发现可以直接把第二维扔到线段树上维护,区间修改区间查询单点更新即可。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    
    
    const int N=1e5+5;
    typedef long long ll;
    int n,a[N],b[N],S;
    
    ll w[N],dp[N],ans;
    vector<int> c;
    struct node
    {
        int a,b;ll w;
    }s[N];
    bool cmp(node x,node y)
    {
        return (x.a+x.b)<(y.a+y.b);
    }
    
    ll maxv[N<<3],lz[N<<3];
    #define ls(k) (k)<<1
    #define rs(k) (k)<<1|1
    #define up maxv[k]=max(maxv[ls(k)],maxv[rs(k)])
    void down(int k)
    {
        if(!lz[k])return ;
        lz[ls(k)]+=lz[k];
        lz[rs(k)]+=lz[k];
        maxv[ls(k)]+=lz[k];
        maxv[rs(k)]+=lz[k];
        lz[k]=0;
    }
    void update(int k,int l,int r,int pos,ll val)
    {
        if(l==r)
        {
            maxv[k]=max(maxv[k],val);
            return ;
        }
        down(k);
        int mid=l+r>>1;
        if(pos<=mid)update(ls(k),l,mid,pos,val);
        else update(rs(k),mid+1,r,pos,val);
        up;
    }
    ll ask(int k,int l,int r,int L,int R)
    {
        if(l>r)return 0;
        if(L<=l&&R>=r)return maxv[k];
        down(k);
        int mid=l+r>>1;ll res=0;
        if(L<=mid)res=max(res,ask(ls(k),l,mid,L,R));
        if(R>mid)res=max(res,ask(rs(k),mid+1,r,L,R));
        return res;
    }
    void change(int k,int l,int r,int L,int R,ll val)
    {
        if(l>r)return ;
        if(L<=l&&R>=r)
        {
            maxv[k]+=val;
            lz[k]+=val;
            return ;
        }
        down(k);
        int mid=l+r>>1;
        if(L<=mid)change(ls(k),l,mid,L,R,val);
        if(R>mid)change(rs(k),mid+1,r,L,R,val);
        up;
    }
    
    int main()
    {
        //freopen("pair.in","r",stdin);
        n=read();
        for(int i=1;i<=n;i++)
            s[i].a=read(),s[i].b=read(),s[i].w=read();
        sort(s+1,s+n+1,cmp);
        for(int i=1;i<=n;i++)
            a[i]=s[i].a,b[i]=s[i].b,c.push_back(a[i]),c.push_back(b[i]),w[i]=s[i].w;
        sort(c.begin(),c.end());vector<int>::iterator it=unique(c.begin(),c.end());
        for(int i=1;i<=n;i++)
        {
            a[i]=lower_bound(c.begin(),it,a[i])-c.begin()+1;
            b[i]=lower_bound(c.begin(),it,b[i])-c.begin()+1;
            S=max(S,max(a[i],b[i]));
        }
        for(int i=1;i<=n;i++)
        {
            if(a[i]>b[i])
                dp[i]=max(dp[i],ask(1,1,S,1,b[i])+w[i]);
            else
            {
                dp[i]=max(dp[i],ask(1,1,S,1,a[i])+w[i]);
                change(1,1,S,a[i],b[i],w[i]);
            }
            update(1,1,S,a[i],dp[i]);
        }
    
        cout<<ask(1,1,S,1,S)<<endl;
        return 0;
    }
    

    C.最小距离

    什么?dj可以跑多源最短路?

    一开始把所有特殊点都扔到堆里,转移的时候记录该点由哪个特殊点转移过来,最后枚举边把答案拼一下就好了。

    因为每个点只会被更新一次,所以跑出来每个点的$dis[]$必然为离它最近的那个特殊点的距离。枚举边的时候,如果两端不是同一个点转移而来,就可以用两个$dis[]$+边权更新答案。

    #include<bits/stdc++.h>
    using namespace std;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return x*f;
    }
    #define pa pair<ll,int>
    const int N=2e5+5;
    typedef long long ll;
    int n,m,p;
    bool isp[N];
    int to[N<<1],head[N],nxt[N<<1],vis[N],sp[N],tot,fr[N];
    ll w[N<<1],ans[N<<1],dis[N];
    void add(int x,int y,ll z)
    {
        to[++tot]=y;
        nxt[tot]=head[x];
        head[x]=tot;
        w[tot]=z;
    }
    void dj()
    {
        priority_queue<pa> q;
        for(int i=1;i<=n;i++)
            dis[i]=1e15,vis[i]=0;
        for(int i=1;i<=p;i++)
            q.push(make_pair(0,sp[i])),fr[sp[i]]=sp[i],dis[sp[i]]=0;
        while(!q.empty())
        {
            int x=q.top().second;q.pop();
            if(vis[x])continue;
            vis[x]=1;
            for(int i=head[x];i;i=nxt[i])
            {
                int y=to[i];
                if(dis[y]>dis[x]+w[i])
                    dis[y]=dis[x]+w[i],fr[y]=fr[x],q.push(make_pair(-dis[y],y));
            }
        }
        for(int i=1;i<=m;i++)
        {
            int x=to[i*2-1],y=to[i*2];
            if(fr[x]!=fr[y])
            {
                ans[fr[x]]=min(ans[fr[x]],dis[x]+w[i*2]+dis[y]);
                ans[fr[y]]=min(ans[fr[y]],dis[x]+w[i*2]+dis[y]);
            }
        }
    }
    int main()
    {
        n=read();m=read();p=read();
        for(int i=1;i<=p;i++)
        {
            int x=read();
            sp[i]=x;
            isp[x]=1;
        }
        for(int i=1;i<=n;i++)
            ans[i]=1e15;
        for(int i=1;i<=m;i++)
        {
            int x=read(),y=read();ll z=read();
            add(x,y,z);add(y,x,z);
        }
        dj();
        for(int i=1;i<=p;i++)
            printf("%lld ",ans[sp[i]]);
        return 0;
    }
    
  • 相关阅读:
    Unique Binary Search Trees 解答
    Unique Paths II 解答
    Unique Paths 解答
    Maximum Subarray 解答
    Climbing Stairs 解答
    House Robber II 解答
    House Robber 解答
    Valid Palindrome 解答
    Container With Most Water 解答
    Remove Duplicates from Sorted List II 解答
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11758607.html
Copyright © 2011-2022 走看看