2018.07.11【2018提高组】模拟C组

前言:OTL


JZOJ 1293 气象牛

题目:

在N个数中选择K个,使K最小并总误差不超过E


分析

选择两点的误差可以预处理,动态规划。
f [ i ] [ j ] = min ( f [ k ] [ j 1 ] + e r r o r [ k ] [ i ] ) ,
f [ i ] [ j ] i j


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define go(i,a,b) for (int i=a;i<b;i++)
using namespace std;
int n,e,a[101],er[101][101],f[101][101],ans[101];
int in(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
int abs(int a){return (a<0)?-a:a;}
int min(int a,int b){return (a<b)?a:b;}
int main(){
    n=in(); e=in(); memset(f,42,sizeof(f));
    fo(i,1,n) a[i]=in();
    fo(i,1,n){
        go(j,1,i) er[0][i]+=abs(a[j]-a[i])<<1;
        fo(j,i+1,n) {
            er[i][n+1]+=abs(a[j]-a[i])<<1;
            go(k,i+1,j) er[i][j]+=abs(2*a[k]-a[i]-a[j]);
        }
    }
    f[0][0]=0; memset(ans,42,sizeof(ans));
    fo(i,1,n) fo(j,1,i) go(k,0,i) f[i][j]=min(f[i][j],f[k][j-1]+er[k][i]);//动态规划
    fo(i,1,n) fo(j,i,n) ans[i]=min(ans[i],f[j][i]+er[j][n+1]);//统计最小值
    fo(i,1,n) if (ans[i]<=e) return !printf("%d %d",i,ans[i]);
}

JZOJ 1294 轻轨

题目

有N个站点,容量为C的列车起点在1号站点,终点在N号站点,有K组牛群,每组数量为 M i ,行程起点和终点分别为 S i E i 1 <= S i < E i <= N ,最多有多少头牛可以搭乘轻轨。


分析

贪心,用终点从小到大排列,有直接模拟的,但是我用的是线段树(zkw线段树),改了一个下午!
一开始让线段树叶子节点设为容量,不断减去每次行程可走的牛的数量,并累加入答案中。
但是zkw线段树没有懒标记,本身就是永久化的标记,所以这就是为什么要上传标记。
使标记=Tree[n]-Tree[n>>1],让每一个节点的值都减除它父亲的值。
这里写图片描述,这就是痛苦!!!


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
struct node{int x,y,w;}e[50001];
int m,n,c,Min[1<<16],M=1,ans;
int in(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
bool cmp(node x,node y){if (x.y!=y.y) return x.y<y.y; else return x.x>y.x;}
int Ask(int h,int t)
{
    int A1=0,A2=0,ans;
    for(h+=M,t+=M;h^t^1&&h<t;h>>=1,t>>=1)//直接跳到叶子节点走到根
    {
        A1+=Min[h];A2+=Min[t];
        if(!(h&1))A1=min(A1,Min[h+1]);//开区间
        if(t&1)A2=min(A2,Min[t-1]);//the same
    }
    ans=min(A1+Min[h],A2+Min[t]);//统计左右的答案
    for(int i=h>>1;i;i>>=1)ans+=Min[i];//走到根
    return ans;
}
void GetChanged(int k)
{
    int temp=min(Min[k<<1],Min[(k<<1)+1]);
    Min[k]+=temp,Min[k<<1]-=temp;Min[(k<<1)+1]-=temp;
}
void Update(int h,int t,int v)
{
    for(h+=M-1,t+=M+1;h^t^1&&h<t;h>>=1,t>>=1)//跳到叶子节点
    {
        if(!(h&1))Min[h+1]+=v;//开区间
        if(t&1)Min[t-1]+=v;//the same
        GetChanged(h>>1);GetChanged(t>>1);//修改父节点的值
    }
    for(h>>=1;h;h>>=1)GetChanged(h);//在父节点不断跳到根并修改值
}
int main(){
    m=in(); n=in(); c=in(); for(;(M<<=1)<n+2;);
    for (int i=1;i<=m;i++) e[i].x=in(),e[i].y=in(),e[i].w=in();
    stable_sort(e+1,e+1+m,cmp); Update(1,n,c);//建树
    for (int i=1;i<=m;i++){
        int g=min(Ask(e[i].x,e[i].y-1),e[i].w);//最多能有多少头牛坐车
        if (g>0) ans+=g,Update(e[i].x,e[i].y-1,-g);//修改线段树
    }
    return !printf("%d",ans);
}

JZOJ 1295 设计

题目

n头牛,有些牛相互喜欢,希望两人的距离 d i s 内,同样也有一些牛相互不喜欢,希望两人的距离 d i s ,求1号牛和N号牛之间最大距离。


分析

对于 1 i < j n ,当希望两人距离 d i s ,可表示为 d i s [ i ] + d i s d i s [ j ]
当希望两人距离 d i s ,可表示为 d i s [ i ] + d i s d i s [ j ] d i s [ j ] d i s d i s [ i ]
所以可以使用spfa或bellman-ford,但是会有负环,所以spfa或bellman-ford在最坏情况要跑O(NM)
那怎么判断负环,对于spfa,每次松弛操作统计某点走过多少次,如果超过n次直接退出(不存在方案),当从第1个点spfa仍然算不出第n个点说明有无数种方案。


代码

#include <cstdio>
#include <cctype>
#include <queue>
#define big 1073741823
using namespace std;
struct node{int y,w,next;}e[20001]; queue<int>q; bool v[1001];
int n,m1,m2,dis[1001],x,y,w,k,ls[1001],cnt[1001];
int in(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
bool spfa(int s){
    q.push(s); v[s]=1; dis[s]=0;
    while (q.size()){
        int x=q.front(); q.pop();
        for (int i=ls[x];i;i=e[i].next)
        if (dis[e[i].y]>dis[x]+e[i].w){
            dis[e[i].y]=dis[x]+e[i].w;
            if (cnt[e[i].y]==n) return 1;
            cnt[e[i].y]=cnt[x]+1;//统计次数
            if (!v[e[i].y]) v[e[i].y]=1,q.push(e[i].y);
        }
        v[x]=0;
    }
    return 0;
}
int main(){
    n=in(); m1=in(); m2=in();
    for (int i=1;i<=n;i++) dis[i]=big;
    while (m1--) {x=in();y=in();w=in();e[++k]=(node){y,w,ls[x]};ls[x]=k;}//加入边
    while (m2--) {x=in();y=in();w=in();e[++k]=(node){x,-w,ls[y]};ls[y]=k;}//加入边
    if (spfa(1)) return !puts("-1");//负环
    else if (dis[n]==big) return !puts("-2"); else return !printf("%d",dis[n]);
}

后续

第三题是这次最的一道题,然后还是错了,233
洛谷 2933 气象测量 洛谷 1607 庙会班车(the same)

猜你喜欢

转载自blog.csdn.net/sugar_free_mint/article/details/81006691