12
18
2015
0

【JSOI2015】最小生成树

难以描述的痛苦。。

我们先预处理出边权大于等于x的边形成的最小生成树或最小生成森林,这些边用主席树存。

那么,如何维护呢,我们从边权从大到小枚举,每次在原来基础上新加一条边后,如果产生了环,就把环中最大的边删去。这样就可以用主席树来维护。

然后对于每个询问l,r,我们只要在root[l]这棵主席树上求边权小于等于r的边权和,这很容易吧。。

这样本题就结束了。orz Gold_7 first blood!!!

代码:

 

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,q,A,p,d,edgenum,cnt,Max,l,r,ans;
int vet[2000010],next[2000010],head[2000010],a[2000010],f[2000010],flag[2000010],root[2000010],e[2000010],dis[2000010];
struct tree{int l,r,sum;}tr[20000010];
struct node{int x,y,z;}ed[2000010];
bool cmp(const node a,const node b){return a.z<b.z;}
int getfather(int x){if (x!=f[x]) f[x]=getfather(f[x]);return f[x];}
void change(int &p,int l,int r,int x,int val)
{
    tr[++cnt]=tr[p];p=cnt;tr[p].sum+=val;
    if (l==r) return;
    int mid=(l+r)>>1;
    if (x<=mid) change(tr[p].l,l,mid,x,val);else change(tr[p].r,mid+1,r,x,val);
}
int ask(int p,int l,int r,int x)
{
    if (r<=x) return tr[p].sum;
    int mid=(l+r)>>1;
    if (x<=mid) return ask(tr[p].l,l,mid,x);else return ask(tr[p].l,l,mid,x)+ask(tr[p].r,mid+1,r,x);
}
void add(int u,int v,int w)
{
    edgenum++;
    vet[edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
    dis[edgenum]=w;
}
int dfs(int u,int t,int fa)
{
    if (u==t&&fa!=-1) return 1;
    for (int e=head[u];e;e=next[e])
    {
        int v=vet[e];
        if ((e+1)/2==fa) continue;
        if (dfs(v,t,(e+1)/2)){flag[(e+1)/2]=1;return 1;}
    }
    return 0;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
        scanf("%d%d%d",&ed[i].x,&ed[i].y,&ed[i].z);
    sort(ed+1,ed+m+1,cmp);
    for (int i=1;i<=n;i++) f[i]=i;
    for (int i=1;i<=m;i++) e[i]=ed[i].z;
    for (int i=m;i>=1;i--)
    {
        root[i]=root[i+1];
        if (getfather(ed[i].x)!=getfather(ed[i].y))
        {
            f[getfather(ed[i].x)]=getfather(ed[i].y);
            a[++a[0]]=i;
            change(root[i],1,m,i,ed[i].z);
        }else
        {
            Max=-1;
            edgenum=0;
            for (int j=1;j<=n;j++) head[j]=0;
            for (int j=1;j<=a[0];j++)
            {
                add(ed[a[j]].x,ed[a[j]].y,ed[a[j]].z);
                add(ed[a[j]].y,ed[a[j]].x,ed[a[j]].z);
            }
            add(ed[i].x,ed[i].y,ed[i].z);
            add(ed[i].y,ed[i].x,ed[i].z);
            for (int j=1;j<=edgenum/2;j++)
                flag[j]=0;
            dfs(ed[i].x,ed[i].x,-1);
            for (int j=1;j<=edgenum/2;j++)
            if (flag[j]) Max=max(Max,dis[j*2]);
            for (int j=1;j<=a[0];j++)
                if (ed[a[j]].z==Max&&flag[j])
                {
                    change(root[i],1,m,a[j],-ed[a[j]].z);
                    for (int k=j;k<a[0];k++) a[k]=a[k+1];
                    a[0]--;
                    break;
                }
            a[++a[0]]=i;
            change(root[i],1,m,i,ed[i].z);
        }
    }
    scanf("%d%d",&q,&A);
    while (q--)
    {
        scanf("%d%d",&p,&d);
        l=ans*A+p;r=l+d;
        l=lower_bound(e+1,e+m+1,l)-e;
        r=upper_bound(e+1,e+m+1,r)-e-1;
        ans=ask(root[l],1,m,r);
        printf("%d\n",ans);
    }
}
Category: 主席树 | Tags:
12
17
2015
0

【JSOI2015】闪电攻击

我们见转换一下模型,把a,b当成x,d当成y,建立平面直角坐标系。首先离散a,b。然后就发现好像可以用区间dp。

f[i][j]表示,处理时间i~j(i,j为离散后的值),需要的最小花费。

那么我们暴力找完全在这个区间里的最高区间(也就是d最大)k。然后枚举k在哪个时间被炸毁。

f[i][j]=min(f[i][j],f[i][h-1]+f[h+1][j]+d[k])(a[k]<=h<=b[k])

解决啦!

代码:

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,cnt,a[310],b[310],d[310],p[610],f[610][610];
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&a[i],&b[i],&d[i]);
		p[++cnt]=a[i];p[++cnt]=b[i];
	}
	sort(p+1,p+cnt+1);
	cnt=unique(p+1,p+cnt+1)-p-1;
	for (int i=1;i<=n;i++)
	{
		a[i]=lower_bound(p+1,p+cnt+1,a[i])-p;
		b[i]=lower_bound(p+1,p+cnt+1,b[i])-p;
	}
	memset(f,10,sizeof(f));
	for (int i=1;i<=cnt;i++)f[i][i-1]=0;
	for (int p=1;p<=cnt;p++)
		for (int i=1;i<=cnt-p+1;i++)
		{
			int j=i+p-1;
			int maxh=0,h=0;
			for (int k=1;k<=n;k++)
			if (a[k]>=i&&b[k]<=j&&d[k]>maxh){maxh=d[k];h=k;}
			if (!h) f[i][j]=0;else
			{
				for (int k=a[h];k<=b[h];k++)
					f[i][j]=min(f[i][j],f[i][k-1]+f[k+1][j]+maxh);
			}
		}
	printf("%d\n",f[1][cnt]);
}
Category: Dynamic Programming | Tags:
12
15
2015
0

【JSOI2015】地铁线路

首先,建图,所有地铁站点建n个点,然后每条线路的站点都建两个点,表示正向反向。

然后加边,易知边权有两个,一个是票价,一个是时间,我们用(票价,时间)来表示。每个站点下站,(0,0),上站(1,0),然后线路上相邻两点,(0,1)。第一问我们只要根据第一问做一次最短路即可,注意会卡spfa,需要用迪迦奥特曼(dijkstra)。第二问,我们根据第一问得到的dis,只需要保留dis[u]+cost[e]==dis[v]的边,易知新图是一张有向无环图,然后我们拓扑排序,根据第二个关键字求最长路即可。

他们都会stl,我只会手打堆。。。

代码:

 

#include<cstdio>
#include<string>
#include<map>
#include<algorithm>
#define INF 1000000000
using namespace std;
struct dui
{
    int x,id;
}d[10000010];
string str;
char s1[55],s2[55];
map<string,int> mp;
int n,s,x,cnt,m,edgenum,Edgenum,S,T,u,p1,p2;
int vet[10000010],next[10000010],head[5000010],tick[10000010],tim[10000010],Vet[10000010],Next[10000010],Head[5000010],Tim[10000010];
int q[5000010],dis[5000010],du[5000010],Min[5000010],flag[5000010];
void add(int u,int v,int w1,int w2)
{
    edgenum++;
    vet[edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
    tick[edgenum]=w1;
    tim[edgenum]=w2;
}
void Add(int u,int v,int w)
{
    Edgenum++;
    Vet[Edgenum]=v;
    Next[Edgenum]=Head[u];
    Head[u]=Edgenum;
    Tim[Edgenum]=w;
}
void shiftdown(int i,int n)
{
    while (i*2<=n)
    {
        int t=i*2;
        if (t+1<=n&&d[t+1].x<d[t].x) t++;
        if (d[i].x>d[t].x)
        {
            q[d[i].id]=t;
            q[d[t].id]=i;
            swap(d[i],d[t]);
            i=t;
        }else break;
    }
}
void shiftup(int i)
{
    while (i>1)
    {
        int t=i/2;
        if (d[i].x<d[t].x)
        {
            q[d[i].id]=t;
            q[d[t].id]=i;
            swap(d[i],d[t]);
            i=t;
        }else break;
    }
}
int main()
{
    scanf("%d%d",&s,&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%s",s1);
        str=s1;
        mp[str]=i;
    }
    cnt=n;
    for (int i=1;i<=s;i++)
    {
        scanf("%d",&x);
        for (int j=1;j<=x;j++)
        {
            scanf("%s",s1);
            str=s1;
            u=mp[str];
            add(cnt+j,u,0,0);
            add(u,cnt+j,1,0);
            add(cnt+j+x,u,0,0);
            add(u,cnt+j+x,1,0);
        }
        cnt+=2*x;
        for (int j=2;j<=x;j++) add(cnt-j-x+1,cnt-j-x+2,0,1);
        for (int j=1;j<x;j++) add(cnt-j+1,cnt-j,0,1);
    }
    scanf("%s%s",s1,s2);
    str=s1;S=mp[str];
    str=s2;T=mp[str];
    for (int i=1;i<=cnt;i++)
        Min[i]=INF;
    Min[S]=0;
    m=cnt;
    for (int i=1;i<=cnt;i++)
    {
        d[i].x=Min[i];
        d[i].id=i;
        q[i]=i;
        shiftup(i);
    }
    for (int i=1;i<=cnt;i++)
    {
        int u=d[1].id;
        flag[d[1].id]=1;
        q[d[m].id]=1;
        d[1]=d[m];
        m--;
        shiftdown(1,m);
        for (int e=head[u];e;e=next[e])
        {
            int v=vet[e];
            if (!flag[v])
            if (Min[u]+tick[e]<Min[v])
            {
                Min[v]=Min[u]+tick[e];
                d[q[v]].x=Min[v];
                shiftup(q[v]);
            }
        }
    }
    if (Min[T]>=INF) {printf("-1\n0\n");return 0;}
    printf("%d\n",Min[T]);
    for (int i=1;i<=cnt;i++)
    {
        for (int e=head[i];e;e=next[e])
        {
            int v=vet[e];
            if (Min[i]+tick[e]==Min[v]) {Add(i,v,tim[e]);du[v]++;}
        }
    }
    for (int i=1;i<=cnt;i++)
        dis[i]=-INF;
    for (int i=1;i<=cnt;i++)if (!du[i]) q[++p2]=i;
    while (p1<p2)
    {   
        p1++;
        u=q[p1];
        for (int e=Head[u];e;e=Next[e])
        {
            int v=Vet[e];
            du[v]--;
            if (du[v]==0) q[++p2]=v;
        }
    }
    dis[S]=0;
    for (int i=1;i<=cnt;i++)
    {
        int u=q[i];
        for (int e=Head[u];e;e=Next[e])
        {
            int v=Vet[e];
            dis[v]=max(dis[v],dis[u]+Tim[e]);
        }
    }
    printf("%d\n",dis[T]);
}
Category: 最短路 | Tags:
12
15
2015
0

【bzoj1430】小猴打架

1430: 小猴打架

Time Limit: 5 Sec  Memory Limit: 162 MB
Submit: 426  Solved: 309
[Submit][Status][Discuss]

Description

一开始森林里面有N只互不相识的小猴子,它们经常打架,但打架的双方都必须不是好朋友。每次打完架后,打架的双方以及它们的好朋友就会互相认识,成为好朋友。经过N-1次打架之后,整个森林的小猴都会成为好朋友。 现在的问题是,总共有多少种不同的打架过程。 比如当N=3时,就有{1-2,1-3}{1-2,2-3}{1-3,1-2}{1-3,2-3}{2-3,1-2}{2-3,1-3}六种不同的打架过程。

Input

一个整数N。

Output

一行,方案数mod 9999991。

Sample Input

4

Sample Output

96

HINT

50%的数据N<=10^3。

100%的数据N<=10^6。

请原谅我刷水,

首先根据prufer序列的性质,互不相同的树的个数有nn-2个,然后对于每一棵树,打架的顺序有(n-1)!种,两者相乘,即为本题答案。。。

代码:

 

#include<cstdio>
#define mod 9999991
using namespace std;
typedef long long ll;
int n;
ll ans;
ll ksm(ll x,int y){ll ans=1;while (y){if (y&1) ans=ans*x%mod;x=x*x%mod;y>>=1;}return ans;}
int main()
{
	scanf("%d",&n);
	ans=ksm(n,n-2);
	for (int i=1;i<n;i++) ans=ans*(ll)i%mod;
	printf("%lld\n",ans);
}
Category: prufer序列 | Tags:
12
15
2015
0

【JSOI2015】最大公约数

这道题我们只要在暴力的基础上改进一下就可以了,由于gcd的值不多,我们只要记录每个点得到某个gcd连续最左能到哪(贪心),这个用map维护就可以了,然后我们从左往右扫,每次维护一下map。

代码:

 

#include<cstdio>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
int n,k;
ll a[100010],g,ans;
set< pair<ll,int> > st,st1;
map<ll,int> mp;
ll gcd(ll x,ll y){if (!y) return x;else return gcd(y,x%y);}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    st.clear();
    for (int i=1;i<=n;i++)
    {
        st1.clear();
        mp.clear();
        for (set< pair<ll,int> > ::iterator it=st.begin();it!=st.end();it++)
        {
            g=(*it).first;
            k=(*it).second;
            g=gcd(g,a[i]);
            if (!mp[g]||k<mp[g])
                mp[g]=k;
            st1.insert(make_pair(g,mp[g]));
            ans=max(ans,(ll)(i-mp[g]+1)*g);
        }
        if (!mp[a[i]]){st1.insert(make_pair(a[i],i));ans=max(ans,a[i]);}
        st.clear();
        for (set< pair<ll,int> > ::iterator it=st1.begin();it!=st1.end();it++)
            st.insert(*it);
    }
    printf("%lld\n",ans);
}
Category: map | Tags:
12
14
2015
0

【JSOI2015】染色问题

容斥原理,我们枚举选i行,选j列,选k中颜色,我们强行让i行以外,j列以外的所有点不能被涂色,那么,只有i*j个格子可以涂色,注意,是可以涂色,也可以不涂色。所以我们的答案就是sigma(c(n,i)*c(m,j)*c(c,k)*(-1)n+m+c-i-j-k*(k+1)i*j)(0<=i<=n,0<=j<=m,0<=k<=c),这样是n3的,能过的。若要log优化,详见Gold_7博客:http://gold7.is-programmer.com/

代码:

 

#include<cstdio>
#include<algorithm>
#define mod 1000000007
using namespace std;
typedef long long ll;
int n,m,c;
ll p[410][160010],C[410][410],ans;
int main()
{
	scanf("%d%d%d",&n,&m,&c);
	for (int i=0;i<=max(max(n,m),c);i++)
		C[i][0]=1;
	for (int i=1;i<=max(max(n,m),c);i++)
		for (int j=1;j<=i;j++)
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
	for (int i=1;i<=c+1;i++)
	{
		p[i][0]=1;
		for (int j=1;j<=n*m;j++)
			p[i][j]=p[i][j-1]*i%mod;
	}
	for (int i=0;i<=n;i++)
		for (int j=0;j<=m;j++)
			for (int k=0;k<=c;k++)
			{
				ll sum=C[n][i]*C[m][j]%mod*C[c][k]%mod*p[k+1][i*j]%mod;
				if ((n+m+c-i-j-k)%2) ans=(ans-sum+mod)%mod;else ans=(ans+sum)%mod;
			}
	printf("%lld\n",ans);
}
Category: 数学 | Tags:
12
12
2015
0

【JSOI2015】圈地

这道题目的模型就是裸的网络流,orz Gold_7,将源点与所有正的方块相连,将所有负的方块与汇点相连,然后将墙连接两个方块,注意要加双向边。

然后假设我们把所有房子都买了,要把两个正负不同的方块分开,要把其中一个不买或者买墙。所以最终答案就是所有房子的利益加起来减去最小割即可。

代码:

 

#include<cstdio>
#include<algorithm>
#define INF 1000000000
using namespace std;
int n,m,u,v,tot,source,sink,vs,cost,edgenum,ans;
int id[210][210],a[210][210],vet[4000010],next[4000010],head[4000010],maxv[4000010],gap[4000010],dis[4000010];
void add(int u,int v,int w)
{
    edgenum++;
    vet[edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
    maxv[edgenum]=w;
}
int dfs(int u,int aug)
{
    if (u==sink) return aug;
    int flow=0,mind=vs;
    for (int e=head[u];e;e=next[e])
    {
        int v=vet[e];
        if (maxv[e]<=0) continue;
        if (dis[u]==dis[v]+1)
        {
            int t=dfs(v,min(aug-flow,maxv[e]));
            maxv[e]-=t;
            if (e&1) maxv[e+1]+=t;else maxv[e-1]+=t;
            flow+=t;
            if (dis[source]>=vs) return flow;
            if (flow==aug) break;
        }
        mind=min(mind,dis[v]);
    }
    if (!flow)
    {
        gap[dis[u]]--;
        if (!gap[dis[u]]) dis[source]=vs;
        dis[u]=mind+1;
        gap[dis[u]]++;
    }
    return flow;
}
int maxflow(int s,int t)
{
    int ans=0;
    gap[0]=vs=t+1;
    source=s;sink=t;
    while (dis[source]<vs) ans+=dfs(source,INF);
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
            id[i][j]=++tot;
    tot++;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            scanf("%d",&a[i][j]);
            if (a[i][j]>0){ans+=a[i][j];add(0,id[i][j],a[i][j]);add(id[i][j],0,0);}else
            if (a[i][j]<0){ans-=a[i][j];add(id[i][j],tot,-a[i][j]);add(tot,id[i][j],0);}
        }
    for (int i=1;i<n;i++)
        for (int j=1;j<=m;j++)
        {
            scanf("%d",&cost);
            u=id[i][j];
            v=id[i+1][j];
            add(u,v,cost);
            add(v,u,cost);
        }
    for (int i=1;i<=n;i++)
        for (int j=1;j<m;j++)
        {
            scanf("%d",&cost);
            u=id[i][j];
            v=id[i][j+1];
            add(u,v,cost);
            add(v,u,cost);
        }
    printf("%d\n",ans-maxflow(0,tot));
}
Category: 网络流 | Tags:
12
12
2015
0

【JSOI2015】最小表示

首先有一个贪心思想,一条边能删就删,这可以证明是完全正确的。那么,我们只要枚举每条边,u->v,这条边能删的条件就是,u到v还有另外一条路径,这等价于存在另外一个点,u能到达这个点且这个点能到达v,所以我们只要用拓扑排序预处理出每个点能到达的点和能到达每个点的集合,用bitset就可以了。

代码:

 

#include<cstdio>
#include<bitset>
using namespace std;
int n,m,u,v,edgenum,p1,p2,ans;
int vet[100010],next[100010],head[30010],l[100010],r[100010],ru[30010],q[30010];
bitset<30010> to[30010],fr[30010];
void add(int u,int v)
{
    edgenum++;
    vet[edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        l[i]=u;r[i]=v;
        add(u,v);ru[v]++;
    }
    for (int i=1;i<=n;i++)
    if (!ru[i]) q[++p2]=i;
    while (p1<p2)
    {
        p1++;
        u=q[p1];
        for (int e=head[u];e;e=next[e])
        {
            int v=vet[e];
            ru[v]--;
            if (ru[v]==0) q[++p2]=v;
        }
    }
    for (int i=1;i<=n;i++)
    {
        to[i][i]=1;
        fr[i][i]=1;
    }
    for (int i=n;i>=1;i--)
    {
        int u=q[i];
        for (int e=head[u];e;e=next[e])
        {
            int v=vet[e];
            to[u]|=to[v];
        }
    }
    for (int i=1;i<=n;i++)
    {
        int u=q[i];
        for (int e=head[u];e;e=next[e])
        {
            int v=vet[e];
            fr[v]|=fr[u];
        }
    }
    for (int i=1;i<=m;i++)
    if ((to[l[i]]&fr[r[i]]).count()>2) ans++;
    printf("%d\n",ans);
}
Category: bitset | Tags:
12
10
2015
0

【bzoj1211】[HNOI2004]树的计数

1211: [HNOI2004]树的计数

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 1778  Solved: 589
[Submit][Status][Discuss]

Description

一个有n个结点的树,设它的结点分别为v1, v2, …, vn,已知第i个结点vi的度数为di,问满足这样的条件的不同的树有多少棵。给定n,d1, d2, …, dn,编程需要输出满足d(vi)=di的树的个数。

Input

第一行是一个正整数n,表示树有n个结点。第二行有n个数,第i个数表示di,即树的第i个结点的度数。其中1<=n<=150,输入数据保证满足条件的树不超过10^17个。

Output

输出满足条件的树有多少棵。

Sample Input

4
2 1 2 1

Sample Output

2

prufer序列。。。一个非常鬼畜的东西。

是这样的,给定一棵树,他的prufer序列就是:

每一次找到编号最小的叶子节点,然后将与这个节点相连的点的编号加入序列,然后把这个叶子节点删除,直到最终剩下两个点为止,

比如这样一棵树

     1

    /  \

   2    5

  /  \    \

 6    3    4

首先找到3,2进入序列,此时序列为{2}

找到4,5进入序列,此时序列为{2,5}

找到5,1进入序列,此时序列为{2,5,1}

找到1,2进入序列,此时序列为{2,5,1,2}

还剩2,6,退出。

可以证明,prufer序列的长度为n-2,且每个节点在序列中出现次数,恰为度-1。

这样就可以做这道题了。就变成一道排列组合题了。答案为:(n-2)!/((d1-1)!(d2-1)!……(dn-1)!)

注意点:

一、注意判无解的情况,n不为1但有一个点的度为0,或者点的度之和不等于n-2

二、尽管ans可以用long long,但中间过程肯定会爆,因此,只要质因数分解就可以了。

代码:

 

#include<cstdio>
using namespace std;
typedef long long ll;
int n,sum,f[210],d[210];
ll ans;
void add(int x,int val)
{
	for (int i=2;i*i<=x;i++)
	if (x%i==0) while (x%i==0){x/=i;f[i]+=val;}
	if (x!=1)f[x]+=val;
}
ll ksm(ll x,int y)
{
	ll ans=1;
	while (y)
	{
		if (y&1) ans*=x;
		x*=x;
		y>>=1;
	}
	return ans;
}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&d[i]);
		if ((!d[i])&&n!=1){printf("0\n");return 0;}
	}
	for (int i=1;i<=n;i++)
		sum+=d[i]-1;
	if (sum!=n-2){printf("0\n");return 0;}
	if (n==1&&d[1]==0){printf("1\n");return 0;}
	for (int i=2;i<=n-2;i++)
		add(i,1);
	for (int i=1;i<=n;i++)
		for (int j=2;j<=d[i]-1;j++)
			add(j,-1);
	ans=1;
	for (int i=1;i<=n;i++)
	if (f[i]) ans*=ksm(i,f[i]);
	printf("%lld\n",ans);
}
Category: prufer序列 | Tags:
12
9
2015
0

【JSOI2015】套娃

做练习赛的时候,想了一个十分鬼畜的贪心,尽管n2,但毫无优化的余地。

比赛结束后,zyd大神的一个贪心,按b从大到小排序,然后依次处理,找到最大的且没被找过的小于当前的in的out,更新答案。证明见zyd:https://www.zybuluo.com/Gold-7/note/236890

之后我们就可以用set来维护了。

代码:

#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
int n,k;ll ans;
struct node{int in,out,b;}doll[200010];
set< pair<int,int> > st;
bool cmp(const node x,const node y){return x.b>y.b;}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%d%d%d",&doll[i].out,&doll[i].in,&doll[i].b);
	sort(doll+1,doll+n+1,cmp);
	ll ans=0;
	for (int i=1;i<=n;i++)
		st.insert(make_pair(-doll[i].out,i));
	for (int i=1;i<=n;i++)
	{
		ans+=(ll)doll[i].b*(ll)doll[i].in;
		set<pair<int,int> >::iterator it=st.lower_bound(make_pair(-doll[i].in,n+1));
		if (it==st.end()) k=0;else k=(*it).second;
		if (k){ans-=(ll)doll[k].out*(ll)doll[i].b;st.erase(it);}
	}
	printf("%lld\n",ans);
}
Category: 贪心 | Tags:

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com