2018.8.6 模拟赛 提高组B

T1
Description

给定一个n个点m条边的有向图,有k个标记点,要求从规定的起点按任意顺序经过所有标记点到达规定的终点,问最短的距离是多少。

Input

第一行5个整数n、m、k、s、t,表示点个数、边条数、标记点个数、起点编号、终点编号。

接下来m行每行3个整数x、y、z,表示有一条从x到y的长为z的有向边。

接下来k行每行一个整数表示标记点编号。

Output

输出一个整数,表示最短距离,若没有方案可行输出-1。

Sample Input

3 3 2 1 1

1 2 1

2 3 1

3 1 1

2

3

Sample Output

3

【样例解释】

路径为1->2->3->1。

Data Constraint

20%的数据n<=10。

50%的数据n<=1000。

另有20%的数据k=0。

100%的数据n<=50000,m<=100000,0<=k<=10,1<=z<=5000。

解题思路

考场上一时抽风写了个最短路套状压初值还没附好,50分。。正解应该是将必须经过的点两两之间的最短路求出来之后dfs

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define int long long

using namespace std;
const int MAXN = 50005;
const int inf = 0x3f3f3f3f3f3f3f3f;

int n,m,k,S,T,go[MAXN],ans=0x3f3f3f3f3f3f3f3f;
int head[MAXN],cnt,tot;
int to[MAXN<<1],nxt[MAXN<<1],val[MAXN<<1];
int dis[15][MAXN],id[MAXN],dp[(1<<11)+5][15];
bool vis[MAXN],must[MAXN],use[MAXN];
queue<int> Q;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

inline void add(int bg,int ed,int w){
    to[++cnt]=ed,nxt[cnt]=head[bg],head[bg]=cnt,val[cnt]=w;
}

inline void spfa(int x){
    memset(vis,false,sizeof(vis));
    dis[++tot][x]=0;vis[x]=1;Q.push(x);
    while(!Q.empty()){
        int x=Q.front();Q.pop();
        for(register int i=head[x];i;i=nxt[i]){
            int u=to[i];
            if(dis[tot][x]+val[i]<dis[tot][u]) {
                dis[tot][u]=dis[tot][x]+val[i];
                if(!vis[u]) {
                    vis[u]=1;
                    Q.push(u);
                }
            }
        }
        vis[x]=0;
    }
}

inline void dfs(int x,int now,int sum){
    if(sum>ans) return;
    if(now==k) {ans=min(ans,sum+dis[id[x]][T]);return;}
    for(register int i=1;i<=k;i++){
        if(use[i]) continue;
        use[i]=1;
        dfs(go[i],now+1,sum+dis[id[x]][go[i]]);
        use[i]=0;
    }
}

signed main(){
    memset(dp,0x3f,sizeof(dp));
    memset(dis,0x3f,sizeof(dis));
    n=rd();m=rd();k=rd();S=rd();T=rd();
    for(register int i=1;i<=m;i++){
        int x=rd(),y=rd(),z=rd();
        add(x,y,z);
    }
    for(register int i=1;i<=k;i++){
        int x=rd();must[x]=1;id[x]=i;go[i]=x;
        spfa(x);
    }
    if(!must[S]) spfa(S),id[S]=k+1;
    for(register int i=1;i<=k;i++){
        memset(use,false,sizeof(use));
        if(dis[id[S]][go[i]]==inf) continue;
        use[i]=1;
        if(must[S] && go[i]!=S) 
            dfs(go[i],2,dis[id[S]][go[i]]);
        else if(!must[S])
            dfs(go[i],1,dis[id[S]][go[i]]);
    }
    if(k==0) ans=dis[id[S]][T];
    if(ans==inf) ans=-1; 
    cout<<ans<<endl;
    return 0;
}

T2

Description

万老师听说某大国很流行穿越,于是他就想写一个关于穿越的剧本。

闲话休提。话说老师穿越到了某一个剑与魔法的大陆。因为如此这般,所以老师从维娜艾那里得到了预言。老师一共被告知了若干件按顺序结算的事件。这些事件分为两类:战役事件(CASE)、穿越回去事件(END)。战役事件可以选择是否参加,参加了之后会获得一定的金钱。每个END事件发生需要至少参加一定数量的战役事件。特别的是,END事件如果满足要求就会强制发生。老师希望在大陆玩个够,所以他要求只有最后一个END事件会发生。老师希望获得最多的金钱,所以求助于你。

Input

第一行一个数N,表示输入文件有多少行。

接下来每一行用空格隔开一个字符和一个整数。字符为“c”表示战役事件,接下来的整数表示这次涨RP顺带有多少钱;字符为“e”表示穿越回去事件,接下来的整数代表至少要涨多少RP。最后一个事件保证是END事件。

Output

第一行一个整数,最多金钱数目。

若不可能则输出-1。

Sample Input

5

c 10

c 12

e 2

c 1

e 2

Sample Output

13

Data Constraint

30%的数据满足 N<=20

60%的数据满足 N<=1,000

100%的数据满足 N<=200,000

每次涨RP事件赏金不超过10,000

穿越事件的要求不超过200,000

解题思路

最后的战役事件竟然能超了最后的end!!!我太菜了还以为能相等。。。写了两个小时的线段树+对拍结果0分。。又是一道由100 变成 0的好题。正解是优先队列+贪心。我用的线段树直接先排序,然后每次挑最大的相当于区间修改,从这个战役之后的第一个一直到倒数第二个这段区间-1。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;
const int MAXN = 200005;
const int inf = 0x3f3f3f3f;

int n,cnt1,cnt2,ans,mn[MAXN<<1];
int sum[MAXN<<1],lazy[MAXN<<1];

struct Data{
    int id,a;
}data[3][MAXN];

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

inline bool cmp(Data A,Data B){
    return A.a>B.a;
}

inline void pushdown(int x,int ln,int rn){
    lazy[x<<1]+=lazy[x];
    lazy[x<<1|1]+=lazy[x];
    sum[x<<1]+=ln*lazy[x];
    sum[x<<1|1]+=rn*lazy[x];
    mn[x<<1]+=lazy[x];
    mn[x<<1|1]+=lazy[x];
    lazy[x]=0;
}

inline void build(int x,int l,int r){
    if(l==r){
        sum[x]=data[2][l].a;
        mn[x]=data[2][l].a;
        lazy[x]=0;
        return ;
    }
    int mid=l+r>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    sum[x]=sum[x<<1]+sum[x<<1|1];
    mn[x]=min(mn[x<<1],mn[x<<1|1]);
}

inline void update(int x,int l,int r,int L,int R,int k){
    if(L<=l && r<=R){
        sum[x]+=k;mn[x]+=k;lazy[x]+=k;
        return ;
    }
    int mid=l+r>>1;
    if(lazy[x]) pushdown(x,mid-l+1,r-mid);
    if(mid>=L) update(x<<1,l,mid,L,R,k);
    if(mid<R)  update(x<<1|1,mid+1,r,L,R,k);
    sum[x]=sum[x<<1]+sum[x<<1|1];
    mn[x]=min(mn[x<<1],mn[x<<1|1]);
}

inline int query(int x,int l,int r,int L,int R){
    if(L<=l && r<=R) return mn[x];
    int ret=0x3f3f3f3f;
    int mid=l+r>>1;
    if(lazy[x]) pushdown(x,mid-l+1,r-mid);
    if(mid>=L) ret=min(ret,query(x<<1,l,mid,L,R));
    if(mid<R)  ret=min(ret,query(x<<1|1,mid+1,r,L,R));
    return ret;
}

int main(){
    n=rd();
    for(register int i=1;i<=n;i++){
        char c;cin>>c;int x=rd();
        if(c=='c') data[1][++cnt1].a=x,data[1][cnt1].id=i;
        else  data[2][++cnt2].a=x,data[2][cnt2].id=i;
    }
    build(1,1,cnt2);
    sort(data[1]+1,data[1]+1+cnt1,cmp);
    for(register int i=1;i<=cnt1;i++){
        int ID=data[1][i].id;int x=data[1][i].a;
        int l=1,r=cnt2;int now;
        while(l<=r){
            int mid=l+r>>1;
            if(data[2][mid].id<ID) l=mid+1;
            else {
                now=mid;
                r=mid-1;
            }
        }
        int res;
        if(now>cnt2-1) res=inf;
        else res=query(1,1,cnt2,now,cnt2-1); 
        if(res>1 || now==cnt2){
            ans+=x;
            if(now!=cnt2)
                update(1,1,cnt2,now,cnt2-1,-1);
        }
    }
    cout<<ans<<endl;
    return 0;
}

T3
Time Limits: 1000 ms Memory Limits: 262144 KB Detailed Limits
Goto ProblemSet

Description

平面上有n个点,求出用这些点可以构成的三角形数。

Input

第一行一个整数n。

接下来n行,每行两个整数,表示点的坐标。

Output

输出仅一个整数,表示所求答案。

Sample Input

5

0 0

1 1

1 -1

-1 -1

-1 1

Sample Output

8

Data Constraint

对于50%的数据,n<=300。

对于100%的数据,n<=3000,坐标的绝对值不超过10^4,保证没有重合的点。

解题思路

这道题跟以前做过的数三角形好像啊,结果还是打的暴力。。用容斥的思想,每次枚举一个点,然后枚举其余点算斜率,之后排个序用组合数学。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#define int long long

using namespace std;
const int MAXN = 3005;
const double inf = 1e10+1;
const double eps = 1e-12;

int n,x[MAXN],y[MAXN],sum,cnt;
double slp[MAXN];

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

signed main(){
    freopen("triangle.in","r",stdin);
    freopen("triangle.out","w",stdout);
    n=rd();sum=n*(n-1)*(n-2)/6;
    for(register int i=1;i<=n;i++)
        x[i]=rd(),y[i]=rd();
    for(register int i=1;i<=n;i++){
        memset(slp,0,sizeof(slp));cnt=0;
        for(register int j=i+1;j<=n;j++){
            if(y[i]==y[j]) slp[++cnt]=inf;
            else if(x[i]==x[j]) slp[++cnt]=-inf;
            else slp[++cnt]=(double)(x[i]-x[j])/(y[i]-y[j]);
        } 
        sort(slp+1,slp+1+cnt);
        int now=1;slp[0]=-inf-5;
        for(register int j=1;j<=cnt;j++){
            if(fabs(slp[j]-slp[j-1])>eps){
                sum-=now*(now-1)/2;
                now=1;
            }
            else now++;
        }
        sum-=now*(now-1)/2;
    }
    printf("%lld",sum);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40448823/article/details/81459417
今日推荐