【XJOI】NOIP2016提高组冲剌题1

【XJOI】NOIP2016提高组冲剌题1

这里写图片描述

第一次打学军的比赛,不知道不要文件输入输出,结果顺利爆零

由于版权问题在此不放入题面

T1:挖金矿

题意: 有一个nm(nm105)的矿场,对于每一列,可以选择往下挖 k(1km)层,求最大平均价值。

打开之前以为NOIP肯定会有送分题的,看到题直接懵了……
这题应该没有不超纲的做法,只能分数规划。
结果我eps设太小TLE了……

题解: 01分数规划

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string.h>
#include<cmath>
#include<cstdlib>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int INF = 2147483647;
const int N = 100010;
const ld eps = 0.00001;
int n,m,K;
double a[N];
inline void Init(int &x){
    char y;while ((y = getchar()) < '0' || y > '9');
    x = y - '0';
    while ((y = getchar()) >= '0' && y <= '9') x = (x << 3) + (x << 1) + y - '0';
}
ld f(ld k){
    ld s = 0;
    for (int i = 0;i < K;i += m){
        ld maxx = a[i] - k,sum = a[i] - k;
        for (int j = 1;j < m;++j) sum += a[i + j] - k,maxx = max(maxx,sum);
        s += maxx;
    }
    return s;
}
int main(){
    scanf("%d%d",&n,&m);K = n * m;
    for (int i = 0;i < K;++i) scanf("%lf",&a[i]);
    ld l = 1,r = 1e9;
    while (l < r){
        ld mid = (l + r) / 2,t = f(mid);
        if (fabs(t) <= eps) {l = r = mid;break;}
        if (t > eps) l = mid;
        else r = mid;
    }
    printf("%.4lf\n",double(l));
    return 0;
}

T2:道路规划

题意: 给出两个1N(N105)的排列,将其一上一下放置,在相等的数间连一条线,求一个元素最多的连线集合的元素数目,使得该集合中连线两两相交。

题解: 很经典的模型,将第一个数组中的数改为其在第二个数组中出现的对应位置,然后用O(nlog2n) 的时间做一遍最长下降子序列

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string.h>
#include<cmath>
#include<cstdlib>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int INF = 2147483647;
const int N = 100010;
int n;
int a[N],mi[N];
struct aa{
    int x,y;
}a1[N],a2[N];
inline void Init(int &x){
    char y;while ((y = getchar()) < '0' || y > '9');
    x = y - '0';
    while ((y = getchar()) >= '0' && y <= '9') x = (x << 3) + (x << 1) + y - '0';
}
bool cmp(aa x,aa y){return x.x < y.x;}
int main(){
    scanf("%d",&n);
    for (int i = 1;i <= n;++i) scanf("%d",&a1[i].x),a1[i].y = i;
    for (int i = 1;i <= n;++i) scanf("%d",&a2[i].x),a2[i].y = i;
    sort(a1 + 1,a1 + n + 1,cmp);sort(a2 + 1,a2 + n + 1,cmp);
    for (int i = 1;i <= n;++i) a[a1[i].y] = a2[i].y;
    int k = 1;mi[1] = a[1];
    for (int i = 2;i <= n;++i){
        int l = 0,r = k;
        while (l < r){
            int mid = (l + r >> 1) + 1;
            if (mi[mid] < a[i]) r = mid - 1;
            else l = mid;
        }
        if (l == k) mi[++k] = a[i];
        else mi[l + 1] = max(mi[l + 1],a[i]);
    }
    cout << k << endl;
    return 0;
}

T3:排队

题意: 给定一个有N(N105)个节点的树形迷宫,且每个节点最多只能容纳一个人,入口为1号节点。每个人行走的策略是优先向下走,若有多条路则选择编号最小的节点继续前进,若没有路则在当前房间停下。

现在有两种操作:1.从入口进入X个人,依次按策略前进,要求输出最后一个人停下时的位置。2.让处于X号节点的人离开,其他人依次按策略补上,问此次操作后有多少人移动了。

操作总数M105

题解: 由于第二种操作每次只能取出一个人,所以通过第一种操作进入树的人总数一定不大。很容易想到的是,进入各节点的顺序是固定的,因此可以用一遍DFS求出这个顺序,然后用一个小根堆维护未被占据的节点,从而实现第一种操作。对于第二种操作,实际上等价于将该节点最远的被占据的祖先节点设为未占据,并输出深度差,用树上倍增可以很容易地实现。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string.h>
#include<cmath>
#include<cstdlib>
#include<vector>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int INF = 2147483647;
const int N = 100010;
int n,m,times = -1,sq;
int conn[N],num[N],la[N],depth[N];
int a[N << 1],nextt[N << 1];
int fa[N][30];
int power[30];
bool inque[N];
struct aa{int x,y;}ed[N << 1];
class heap{
    private : int data[N];
    public :
    int tail;
    void push(int x){
        data[++tail] = x;
        for (int t = tail;t > 1 && data[t] < data[t >> 1];t >>= 1)
            swap(data[t],data[t >> 1]);
    }
    int top(){return data[1];}
    void pop(){
        data[1] = data[tail--];
        for (int t = 1;(t << 1) <= tail;){
            int k1 = data[t << 1],k2 = t << 1;
            if (k2 < tail && data[k2 + 1] < k1) k1 = data[k2 + 1],++k2;
            if (k1 < data[t]) swap(data[t],data[k2]),t = k2;
            else break;
        }
    }
}q;
inline void Init(int &x){
    char y;while ((y = getchar()) < '0' || y > '9');
    x = y - '0';
    while ((y = getchar()) >= '0' && y <= '9') x = (x << 3) + (x << 1) + y - '0';
}
bool cmp(aa x,aa y){return x.x < y.x || x.x == y.x && x.y > y.y;};
inline void ED(int x,int y){
    nextt[++times] = conn[x];
    conn[x] = times;
    a[times] = y;
}
void DFS(int now,int last,int dep){
    fa[now][0] = last;depth[now] = dep;
    for (int t = 1;dep - power[t] > 0;++t)
        fa[now][t] = fa[fa[now][t - 1]][t - 1];
    for (int k = conn[now];~k;k = nextt[k])
        if (a[k] != last) DFS(a[k],now,dep + 1);
    num[now] = ++times;la[times] = now;
}
void OP1(int x){
    for (int i = 1;i < x;++i) inque[q.top()] = 0,q.pop();
    printf("%d\n",la[q.top()]);inque[q.top()] = 0;q.pop();
}
void OP2(int x){
    int l = 1,r = x;
    for (int i = sq;~i;--i){
        if (depth[x] - power[i] <= 0) continue;
        if (!inque[num[fa[r][i]]]) r = fa[r][i];
        else l = fa[r][i];
    }
    if (l != r){if (!inque[num[l]]) r = l;else l = r;}
    inque[num[l]] = 1;q.push(num[l]);printf("%d\n",depth[x] - depth[l]);
}
int main(){
    scanf("%d%d",&n,&m);power[0] = 1;for (int i = 1;i < 29;++i) power[i] = power[i - 1] << 1;
    for (int i = 1;i < n;++i) scanf("%d%d",&ed[i].x,&ed[i].y);
    for (int i = 1;i < n;++i) ed[n + i - 1].x = ed[i].y,ed[n + i - 1].y = ed[i].x;
    sort(ed + 1,ed + n + n - 1,cmp);memset(conn,-1,sizeof(conn));
    for (int i = 1;i < n + n - 1;++i) ED(ed[i].x,ed[i].y);
    times = 0;DFS(1,0,1);q.tail = 0;
    for (int i = 1;i <= n;++i) q.push(i);
    memset(inque,1,sizeof(inque));sq = log(double(n)) / log(2.0);
    for (int i = 1;i <= m;++i){
        int op,x;scanf("%d%d",&op,&x);
        if (op == 1) OP1(x);
        else OP2(x);
    }
    return 0;
}

总结:

题解已更新。
距离复赛还剩四周,继续努力!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值