[CSP-S模擬測試92]題解

A.數列

顯然每一個數的答案是互相獨立的,直接擴歐求解。咱們須要最小化$ax+by=gcd(a,b)$中的$|x|+|y|$,而顯然當x或y靠近0時答案可能最優,列個不等式求一下便可。node

能$O(1)$千萬不要懶。ios

#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\n",ans);
	return 0;
}

 

B.數對

可任意排序看似難以解決,但考慮一下$a$和$b$之間的限制,不難發現若是$a_i<b_j \ \ and\ \ b_i<a_j$,$i$應當儘量在$j$前面,若是剛好相反那麼$i$就應該在後面,至於剩下的狀況,怎麼排都是同樣的。那麼按$a+b$排序就能保證選取必定最優。c++

以後就是原題了。線段樹維護dp板子。git

(突然發現我好像沒寫過以前那題的題解 懶癌發做失敗)spa

那麼如今問題就轉化成了給你一個二元組序列,要求你按順序選幾個二元組,在知足全部$a_i \leq b_j \ (i<j)$的狀況下最大化權值。blog

設$dp[i][j]$表示當前考慮到i而且選了它,且所選集合中最大的$a[]$值爲j的最大收益。($a[]$和$b[]$固然是要離散化的= =)排序

暴力$O(n^3)$轉移就很顯然了:get

#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;
}

 

而後你發現能夠直接把第二維扔到線段樹上維護,區間修改區間查詢單點更新便可。string

#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能夠跑多源最短路?it

一開始把全部特殊點都扔到堆裏,轉移的時候記錄該點由哪一個特殊點轉移過來,最後枚舉邊把答案拼一下就行了。

由於每一個點只會被更新一次,因此跑出來每一個點的$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;
}
相關文章
相關標籤/搜索