tEST 3 for NOIP

头更大

这个9月完就要去集训搞NOIP了。。。

9月30天也就3次测试。。。为防万一我还是每次测试玩都写个总结。。


第三题强势暴力,第二题浪了。
第一题。。。。。。

mmp读入优化有毒!!!
mmp读入优化有毒!!!
mmp读入优化有毒!!!

跳高

题目背景
SOURCE:NOIP2015-HN-CJZX

题目描述
r64 喜欢跳高。但是他的技术并不好,所以他想好好练习一下。

练习场上有一个个高度不一定一样的平台,如图所示。最底下的是地板,高度为 0 。有 n 个平台,第 i 个平台的高度为 hi (hi≥0)。作为一名跳高爱好者,r64 希望跳到尽可能高的平台。

但是 r64 技术不好,他的最大跳跃高度是 Δh 。也就是说,如果 r64 当前高度为 h1 ,某个平台的高度为 h2 (h2≥h1 ),那么 r64 能跳到这个平台,当且仅当 h2≤h1+Δh 。之后,r64 的高度变成 h2 。

除此之外,r64 规定他自己不能多次跳上同一个平台。

r64 想知道,他最多能跳多高。并且,在跳得尽量高的前提下,他最多经过多少个平台,以及最少经过多少个平台。

输入格式
输入第一行有两个正整数 n 和 Δh ,分别表示平台个数和 r64 的最大跳跃高度。
接下来一行有 n 个非负整数 h1,h2,…,hn,表示每个平台的高度。

输出格式
输出只包括一行三个整数 maxh,maxjump,minjump,分别表示 r64 能跳的最大高度、他跳到高度 maxh 所经过的平台数量的最大值与最小值。

样例数据 1
输入  [复制]

5 2
1 2 3 5 3
输出

5 5 3
样例数据 2
输入  [复制]

9 1
1 2 3 4 6 7 8 9 10
输出

4 4 4
备注
【样例1说明】
经过平台数量为 5 的一种跳法是:0→1→2→3→3→5。
经过平台数量为 3 的一种跳法是:0→2→3→5。当然,0→1→3→5也是允许的。

【数据说明】
一共 20 个测试点,每个测试点的特征如下表所示。
这里写图片描述

。。。考试时一群人死在没有加读入优化上。。。但更不能理解的是曾老电脑的cin比scanf快了好几倍?????
用scar_lyw的话来说,鬼畜了。

MY/STD.CPP

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<string>
#include<cstring>
#include<ctime>
using namespace std;

int n,m;
int a[1000500],f[1000500];
int maxh=0,maxj=0,minj=0;

int main()
{
//  freopen("jump.in","r",stdin);
//  freopen("jump.out","w",stdout); 
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cin >> n >> m;
    for(int i=1;i<=n;i++)
        cin >> a[i];
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
    {
        if(a[i]-a[i-1]>m)break;
        else{
            maxh = a[i];
            maxj += 1;
            if(a[i]!=a[i-1])
              f[i] = a[i];
        }
    }
    memset(a,127,sizeof(a));
    a[0] = 0;
    int head=0,tail=0;
    while(f[tail]!=maxh)
    {
        tail++;
        while(f[head]+m<f[tail])head++;
        a[tail]=min(a[tail],a[head]+1);
    }

    minj = 999999999;
    while(f[head]+m<f[tail])head++;
        minj = min(minj,a[head]+1);

    if(maxh==0)minj=0;
    cout<<maxh<<" "<<maxj<<" "<<minj<<endl;
}

证明

题目背景
SOURCE:NOIP2015-HN-CJZX

题目描述
H 教授是一位德高望重的教授,也是计算机科学界的权威。他对问题总有独特而深刻的见解,治学严谨,是学术界的带头人。

在一次科学家大会上,H 教授在黑板上写下了 n 个式子 x1,x2,…,xn ,并向参加会议的所有科学家证明了:如果 x1=x2=…=xn,那么可以证明 P=NP 。可是,毕竟人无完人,H 教授对其中的任意两个式子是否相等都说不清。他把这个问题抛给了全世界的科学家们。

令人激动的是,没过多久,H 教授就收到了数学家们发来的 m 封 email ,第 i 封 email 写到,发信人已经证明了 ∀li≤a< ri,xa=xa+1,即 xli,x(li)+1,…,xri 两两相等。但是,这些证明是有版权的,如果 H 教授需要使用这些证明,那么需要向提供证明的人支付 ci 元稿费。

H 教授希望通过这些信息证明出 P=NP 。但是,H 教授最近手头拮据,所以希望支付最小的费用。

输入格式
输入的第一行是两个整数 n,m。
接下来 m 行,每行三个整数 li,ri,ci (1≤li≤ri≤n),代表第 i 位数学家的证明及其稿费。

输出格式
输出只包含一个整数,表示 H 教授至少要支付多少元稿费,才能证明出 P=NP。如果根据现有条件无法证明 P=NP ,请输出-1。

样例数据 1
输入  [复制]

9 3
1 3 101010
4 6 98889
7 9 76543
输出

-1
样例数据 2
输入  [复制]

9 7
1 5 3
3 6 8
5 8 4
4 7 6
2 3 7
7 9 2
6 7 5
输出

9
备注
【样例1说明】
就算把所有的数学家都叫上,仍然证明不了 x3=x4 和 x6=x7。

【样例2说明】
第一位数学家可以证明 x1=x2=x3=x4=x5。
第三位数学家可以证明 x5=x6=x7=x8。
第六位数学家可以证明 x7=x8=x9。
这三位数学家是足够的,并且只需 9 元稿费。可以证明没有更优的方案。

【数据说明】
所有测试点的数据范围如下表所示。
这里写图片描述

一开始觉得标答无望就乱打了个有向图套Dijkstra(边多)后来发现还差点打成标答。。。但是建边的时候瓜了。
额我具体怎么挂的自己看吧。

MY.CPP

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<string>
#include<cstring>
#include<ctime>
#include<queue>
using namespace std;

const int kkk=100055;

int n,m;
struct nde{
    int l,r,c;
}s[kkk];

bool comp(const nde &a,const nde &b)
{if(a.l==b.l)return a.r<b.r;return a.l<b.l;}

struct node{
    int u,v,val,next;
}side[6*kkk];

int cnt=0,first[kkk];
void addedge(int u,int v,int val)
{
    cnt += 1;
    side[cnt].u = u;
    side[cnt].v = v;
    side[cnt].val = val;
    side[cnt].next = first[u];
    first[u] = cnt;
}

bool visit[kkk];
long long dis[kkk];
long long dij(int s,int t)
{
    memset(dis,126,sizeof(dis));
    priority_queue< pair<long long,int> >que;
    que.push(make_pair(0,s));
    dis[s] = 0;
    long long gud = dis[t];
    while(!que.empty())
    {
        pair<int,int> k = que.top();
        que.pop();
        visit[k.second] =true;
        for(int j=first[k.second];j;j=side[j].next)
        {
            int v = side[j].v;
            if(!visit[v] && dis[v]>dis[k.second]+side[j].val)
            {
                dis[v] = dis[k.second]+(long long)side[j].val;
                que.push(make_pair(-dis[v],v));
            }
        }
    }
    if(dis[t]==gud)return -1;
    return dis[t];
}

int main()
{
//  freopen("proof.in","r",stdin);
//  freopen("proof.out","w",stdout);    
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    cin>>s[i].l>>s[i].r>>s[i].c;
    sort(s+1,s+m+1,comp);

    int head=1,tail=1;
    addedge(s[tail].l,s[tail].r,s[tail].c); 

    while(tail!=m)
    {
        tail++;   addedge(s[tail].l,s[tail].r,s[tail].c);   

        while(s[head].r<s[tail].l)head++;
        for(int i=head;i<tail;i++)
            addedge(s[i].l,s[tail].l,s[i].c);   
    }
    cout << dij(1,n) << endl;
}

哦对了标答还要记得套个离散化还有把数组开到1e6不然会卡四个点。

STD.CPP

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<string>
#include<cstring>
#include<ctime>
#include<queue>
using namespace std;

const int kkk=1000055;

int n,m;
struct nde{
    int l,r;
    long long c;
}s[kkk];

bool comp(const nde &a,const nde &b)
{if(a.l==b.l)return a.r<b.r;return a.l<b.l;}

struct node{
    int u,v,val,next;
}side[6*kkk];

int cnt=0,first[kkk];
void addedge(int u,int v,int val)
{
    cnt += 1;
    side[cnt].u = u;
    side[cnt].v = v;
    side[cnt].val = val;
    side[cnt].next = first[u];
    first[u] = cnt;
}

bool visit[kkk];
long long dis[kkk];
long long dij(int s,int t)
{
    memset(dis,126,sizeof(dis));
    priority_queue< pair<long long,int> >que;
    que.push(make_pair(0,s));
    dis[s] = 0;
    long long gud = dis[t];
    while(!que.empty())
    {
        pair<int,int> k = que.top();
        que.pop();
        visit[k.second] =true;
        for(int j=first[k.second];j;j=side[j].next)
        {
            int v = side[j].v;
            if(!visit[v] && dis[v]>dis[k.second]+side[j].val)
            {
                dis[v] = dis[k.second]+(long long)side[j].val;
                que.push(make_pair(-dis[v],v));
            }
        }
    }
    if(dis[t]==gud)return -1;
    return dis[t];
}

int num[2*kkk],tot=0;
int main()
{
//  freopen("proof.in","r",stdin);
//  freopen("proof.out","w",stdout);

    cin>>n>>m;

    for(int i=1;i<=m;i++)
    {
        cin>>s[i].l>>s[i].r>>s[i].c;    
        num[++tot]=s[i].l;num[++tot]=s[i].r;
    }

    num[++tot]=1;   num[++tot]=n;

    sort(num+1,num+tot+1);
    n = unique(num+1,num+tot+1)-num-1;
    tot = n;

    for(int i=1;i<=m;i++)
    {
        s[i].l=lower_bound(num+1,num+tot+1,s[i].l)-num;
        s[i].r=lower_bound(num+1,num+tot+1,s[i].r)-num;
        addedge(s[i].l,s[i].r,s[i].c);  
    }

    for(int i=2;i<=n;i++)
        addedge(i,i-1,0);

    cout << dij(1,n) << endl;
}

题目背景
SOURCE:NOIP2015-HN-CJZX

题目描述
树(tree)是指由 n 个点,n-1 条边构成的连通无向图。如果有一棵树,它的每一条边 (u,v) 都有一个权值 lu,v ,我们把这样的树称作带权树(weighted tree)。

我们知道对于树上的任意两个点,他们之间的路径是唯一的。对于两个点 u,v 来说,我们可以算出 u 与 v 之间的路径上所有边权之和,将其称作 u 与 v 之间路径的长度,记作 d(u,v)。

你的任务是计算:

输入格式
输入第一行为一个正整数 n 。
接下来 n-1 行,每行三个非负整数 x,y,w,表示点 x 与点 y 之间有一条权值为 w 的一条边。
保证输入的图是一棵树。

输出格式
输出仅包含一行一个数,即答案。因为结果可能很大,请将答案模 100 000 007 输出。

样例数据 1
输入  [复制]

4
1 2 4
1 3 4
1 4 4
输出

72
样例数据 2
输入  [复制]

10
1 2 1320321
2 3 4687651
3 4 1321357
4 5 6651332
5 6 5746513
6 7 5674687
7 8 7451216
8 9 7789965
9 10 8765134
输出

28244404
备注
【样例1说明】
d(1,2)+d(1,3)+d(1,4)=4+4+4=12
d(2,1)+d(2,3)+d(2,4)=4+8+8=20
d(3,1)+d(3,2)+d(3,4)=4+8+8=20
d(4,1)+d(4,2)+d(4,3)=4+8+8=20
所以答案为:12+20+20+20=72。

【数据范围】
共 20 个测试点,其数据特征如下表所示。
这里写图片描述对所有数据,有 1≤n≤3×105,0≤w<100 000 007。数据有一定梯度。

只能说自己考试时脑抽的吓人。正解很显然枚举每一条边,把边左右两边的点的个数和边权值乘起来就行了。
然而当我想到这一步的时候。。。我。竟。然。不。知。道。怎。么。求。一。条。边。两。边。的。点。的。个。数。了。。。。。然后打了个LCA的超级大暴搜。。。
后来看了一眼题解才发现一遍DFS完事。。。。。。

MY.CPP

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
using namespace std;

int n,m,x,y,u,v,val;
struct node{
    int u,v,val,next;
}side[600010];

int cnt=0,first[300010];
void addedge(int u,int v,int val)
{
    cnt += 1;
    side[cnt].u = u;
    side[cnt].v = v;
    side[cnt].val = val;
    side[cnt].next = first[u];
    first[u] = cnt;
}

bool visit[300010];
int dep[300010];
long long dis[300010];
int f[300010][20];
void dfs(int root)
{
    visit[root] = true;
    for(int i=first[root];i;i=side[i].next)
    {
      int u = side[i].u;    int v = side[i].v;
      if(!visit[v])
      {
        f[v][0] = u;
        dep[v] = dep[u] + 1;
        dis[v] = dis[u] + (long long)side[i].val;
        dfs(v);
      }
    }
}

#define maxx 300005
void initi(int root)
{
    memset(dep,0,sizeof(dep));
    dis[root] = 0;
    dep[root] = 0;
    dfs(root);
    for(int i=1;i<=18;i++)
      for(int j=1;j<=maxx;j++)
        if(f[j][i-1])   
          f[j][i] = f[f[j][i-1]][i-1];
}

int lca(int x,int y)
{
    if(dep[x]<dep[y]){int k=x;x=y;y=k;}
    int foot = dep[x] - dep[y];
    for(int i=18;i>=0;i--)
      if(foot>=(1<<i))  foot -= (1<<i),x=f[x][i];
    if(x==y)    return x;
    for(int i=18;i>=0;i--)
      if(f[x][i]!=f[y][i])  x=f[x][i],y=f[y][i];
    return f[x][0];
}

int main()
{
//  freopen("tree.in","r",stdin);
//  freopen("tree.out","w",stdout); 
    cin >> n ;
    for(int i=1;i<n;i++)
    {
        cin >> u >> v >> val;
        addedge(u,v,val);
        addedge(v,u,val);
    }

    initi(1);
    long long ans = 0;

    for(int i=1;i<n;i++)
      for(int j=i+1;j<=n;j++)
      {
        ans += 2*(dis[i]+dis[j]-2*dis[lca(i,j)]);
        ans %= 100000007;
      }

    cout << ans << endl;
}

STD.CPP

#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<string>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<set>
#include<map>
using namespace std;

const int kkk=300005;
const int nod=100000007;

long long n,ans,son[kkk],fa[kkk];

long long cnt,first[kkk];
struct node
{
    int u,v,next;
    long long val;
}side[2*kkk];

inline long long read()
{
    long long i=0;  char ch;
    ch=getchar();
    while(ch>'9'||ch<'0')ch=getchar();
    while(ch<='9'&&ch>='0')
    {
        i=(i<<3)+(i<<1)+ch-'0';
        ch=getchar();
    }
    return i;
}

inline void add(int x,int y,long long val)
{
    side[++cnt].v=y;
    side[cnt].u=x;
    side[cnt].next=first[x];
    side[cnt].val=val;
    first[x]=cnt;
}

inline void dfs(int root)
{
    for(int i=first[root];i;i=side[i].next)
        if(side[i].v!=fa[root])
        {
            fa[side[i].v]=root;
            dfs(side[i].v);
            son[root]+=son[side[i].v];
        }
}

int main()
{
    n=read();   
    for(int i=2;i<=n;++i)
    {
        int x,y;    long long val;  
        x=read();y=read();val=read();
        add(x,y,val);   add(y,x,val);   
        son[i]=1;
    }

    son[1]=1;   dfs(1); 
    for(int i=1;i<=cnt;++i)
    {
        int o=min(son[side[i].v],son[side[i].u]);
        long long j=(long long)o*(long long)(n-o);

        j%=nod; j*=side[i].val;
        j%=nod; ans+=j;
        ans%=nod;
    }

    cout<<ans<<endl;    
    return 0;
}

感想

暴力出奇迹
然后平时多做些有关树和Dp的题
(话说Dp我还没搞。。。orz)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值