2018.8.6提高B组模拟考试

爆零了...爽到

 

T1 题意简述:jzoj3470

 

Description
给定一个n个点m条边的有向图,有k个标记点,要求从规定的起点按任意顺序经过所有标记点到达规定的终点,问最短的距离是多少。
Input
第一行5个整数n、m、k、s、t,表示点个数、边条数、标记点个数、起点编号、终点编号。
接下来m行每行3个整数x、y、z,表示有一条从x到y的长为z的有向边。
接下来k行每行一个整数表示标记点编号。
Output
输出一个整数,表示最短距离,若没有方案可行输出-1。
Data Constraint
20%的数据n<=10。
50%的数据n<=1000。
另有20%的数据k=0。
100%的数据n<=50000,m<=100000,0<=k<=10,1<=z<=5000。

 

   解题思路:k遍dijkstra或SPFA+爆搜即可。由于k只有10,因此比状压dp还要快些。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
using namespace std;
ll n,m,k,s,t,cnt,ans=INF,head[50001];
ll mp[50001],pnt[11],len[20][20];
ll dis[50001],vis[50001];
struct uio{
    ll nxt,to,val;
}edge[100001];
void add(ll x,ll y,ll z)
{
    edge[++cnt].nxt=head[x];
    edge[cnt].to=y;
    edge[cnt].val=z;
    head[x]=cnt;
}
void spfa(ll x)
{
    memset(dis,0x3f,sizeof(dis));
    queue<ll> que;
    while(!que.empty()) que.pop();
    dis[x]=0;
    vis[x]=1;
    que.push(x);
    while(!que.empty())
    {
        ll now=que.front();
        que.pop();
        vis[now]=0;
        for(ll i=head[now];i;i=edge[i].nxt)
        {
            ll y=edge[i].to;
            if(dis[y]>dis[now]+edge[i].val)
            {
                dis[y]=dis[now]+edge[i].val;
                if(!vis[y]) vis[y]=1,que.push(y);
            }
        }
    }
}
void dfs(ll x,ll now,ll l)
{
    if(l>ans) return;
    if(x==k)
    {
        ans=min(ans,l+len[now][k+1]);
        return;
    }
    for(ll i=1;i<=k;i++)
        if(!vis[i]&&i!=now&&len[now][i]!=INF)
            vis[i]=1,dfs(x+1,i,l+len[now][i]),vis[i]=0;
}
int main()
{
    scanf("%lld%lld%lld%lld%lld",&n,&m,&k,&s,&t);
    for(ll i=1;i<=m;i++)
    {
        ll u,v,w;
        scanf("%lld%lld%lld",&u,&v,&w);
        add(u,v,w);
    }
    for(ll i=1;i<=k;i++)
    {
        scanf("%lld",&pnt[i]);
        mp[pnt[i]]=++mp[0];
    }
    spfa(s);
    for(ll i=1;i<=k;i++)
        len[0][mp[pnt[i]]]=dis[pnt[i]];
    if(!k)
    {
        if(dis[t]==INF) printf("-1\n");
        else printf("%lld\n",dis[t]);
        return 0;
    }
    for(ll i=1;i<=k;i++)
    {
        spfa(pnt[i]);
        len[mp[pnt[i]]][k+1]=dis[t];
        for(ll j=1;j<=k;j++)
            len[mp[pnt[i]]][mp[pnt[j]]]=dis[pnt[j]];
    }
    dfs(0,0,0);
    if(ans==INF) printf("-1\n");
    else printf("%lld\n",ans);
    return 0;
}

 


 

T2 题意简述:jzoj3487

 

Description
万老师听说某大国很流行穿越,于是他就想写一个关于穿越的剧本。
闲话休提。话说老师穿越到了某一个剑与魔法的大陆。因为如此这般,所以老师从维娜艾那里得到了预言。老师一共被告知了若干件按顺序结算的事件。这些事件分为两类:战役事件(CASE)、穿越回去事件(END)。战役事件可以选择是否参加,参加了之后会获得一定的金钱。每个END事件发生需要至少参加一定数量的战役事件。特别的是,END事件如果满足要求就会强制发生。老师希望在大陆玩个够,所以他要求只有最后一个END事件会发生。老师希望获得最多的金钱,所以求助于你。   
Input
第一行一个数N,表示输入文件有多少行。
接下来每一行用空格隔开一个字符和一个整数。字符为“c”表示战役事件,接下来的整数表示这次涨RP顺带有多少钱;字符为“e”表示穿越回去事件,接下来的整数代表至少要涨多少RP。最后一个事件保证是END事件。   
Output
第一行一个整数,最多金钱数目。
若不可能则输出-1。
Data Constraint
30%的数据满足 N<=20
60%的数据满足 N<=1,000
100%的数据满足 N<=200,000
每次涨RP事件赏金不超过10,000
穿越事件的要求不超过200,000   

 

   解题思路:小根堆维护所有事件即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
using namespace std;
int n,ans;
priority_queue<int,vector<int>,greater<int> > que;
int main()
{
    scanf("%d\n",&n);
    for(int i=1;i<n;i++)
    {
        char c;int u;
        scanf("%c %d\n",&c,&u);
        if(c=='c') que.push(u);
        else while(que.size()>=u) que.pop();
    }
    char c;int u;
    scanf("%c %d",&c,&u);
    if(que.size()<u) {printf("-1\n");return 0;}
    while(!que.empty())
        ans+=que.top(),que.pop();
    printf("%d\n",ans);
    return 0;
}

 


 

T3 题意简述:jzoj3493

 

Description
平面上有n个点,求出用这些点可以构成的三角形数。
Input
第一行一个整数n。
接下来n行,每行两个整数,表示点的坐标。
Output
输出仅一个整数,表示所求答案。
Data Constraint
对于50%的数据,n<=300。
对于100%的数据,n<=3000,坐标的绝对值不超过10^4,保证没有重合的点。

 

   解题思路:枚举每个点,把其它点与这个点的斜率计算出来,将斜率相同的合并,计算组合数,用总方

             案数减去计算出的组合数即可。

             如何去重?只需在第二重循环时只枚举i+1~n的点且只把包含i的不合法情况去除即可。

             考虑以下例子:编号为1 2 3 4的点排成一条直线。

             枚举到1时,只把包含1的不合法情况去除。在本例子中,即去除(1,2,3)(1,2,4)(1,3,4)。

             枚举到2时,只把包含2的不合法情况去除。在本例子中,即去除(2,3,4)。

             以此类推,只需计算C(n,3)-∑(i=1~n)∑(j=i+1~n)C(k,2) 其中k表示相同斜率的数量。

             感谢ErkkiErkko大佬和Menteur_Hxy大佬!!!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
using namespace std;
ll n,cnt,ans;
double k[10001],a[10001],b[10001];
int main()
{
    freopen("triangle.in","r",stdin);
    freopen("triangle.out","w",stdout);
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
        scanf("%lf%lf",&a[i],&b[i]);
    for(ll i=1;i<=n;i++)
    {
        cnt=0,memset(k,0xc0,sizeof(k));
        for(ll j=i+1;j<=n;j++)
            if(a[i]==a[j]) k[++cnt]=INF;
            else k[++cnt]=(b[j]-b[i])/(a[j]-a[i]);
        sort(k+1,k+1+cnt);
        ll tmp=1;
        for(ll j=2;j<=cnt+1;j++)
            if(k[j]==k[j-1]) tmp++;
            else ans+=tmp*(tmp-1)/2,tmp=1;
    }
    printf("%lld",n*(n-1)*(n-2)/6-ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/water-radish/p/9431310.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值