【XJOI】NOIP2016提高组冲剌题1
第一次打学军的比赛,不知道不要文件输入输出,结果顺利爆零。
由于版权问题在此不放入题面
T1:挖金矿
题意: 有一个n∗m(n∗m≤105)的矿场,对于每一列,可以选择往下挖 k(1≤k≤m)层,求最大平均价值。
打开之前以为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:道路规划
题意: 给出两个1∼N(N≤105)的排列,将其一上一下放置,在相等的数间连一条线,求一个元素最多的连线集合的元素数目,使得该集合中连线两两相交。
题解: 很经典的模型,将第一个数组中的数改为其在第二个数组中出现的对应位置,然后用O(n⋅log2n) 的时间做一遍最长下降子序列。
代码:
#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(N≤105)个节点的树形迷宫,且每个节点最多只能容纳一个人,入口为1号节点。每个人行走的策略是优先向下走,若有多条路则选择编号最小的节点继续前进,若没有路则在当前房间停下。
现在有两种操作:1.从入口进入
操作总数
题解: 由于第二种操作每次只能取出一个人,所以通过第一种操作进入树的人总数一定不大。很容易想到的是,进入各节点的顺序是固定的,因此可以用一遍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;
}
总结:
题解已更新。
距离复赛还剩四周,继续努力!