Noip 真题题解合集
由于我太懒了呀,再加上最近天天考试,就合起来写吧。(长期更新(
Car的旅行路线
noip远古题
难度:一般
思路:
做法很显然,建图之后跑最短路即可,但是预处理比较(非常)麻烦。
Code:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
using namespace std;
//Mystery_Sky
//
#define M 100100
#define INF 0x3f3f3f3f
#define ll long long
inline int read()
{
int x=0,f=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
struct Edge{
int to, next;
double w;
}edge[M];
struct cc{
int x[5], y[5];
int T;
}city[105];
int o, n, a, b, t;
int head[10001], cnt, vis[10001];
double dis[5][10001];
inline void add_edge(int u, int v, double w){edge[++cnt].to = v; edge[cnt].next = head[u]; edge[cnt].w = w; head[u] = cnt;}
inline double dist(int x1, int y1, int x2, int y2)
{
double xx = x1 - x2, yy = y1 - y2;
return sqrt(xx * xx + yy * yy);
}
inline int main_p(double d1, double d2, double d3)
{
d1 = d1 * d1, d2 = d2 * d2, d3 = d3 * d3;
if(d1 + d3 - d2 < 1e-2) return 2;
if(d2 + d3 - d1 < 1e-2) return 3;
if(d1 + d2 - d3 < 1e-2) return 1;
return 0;
}
inline void build_highway(int id)
{
int x1 = city[id].x[1], y1 = city[id].y[1];
int x2 = city[id].x[2], y2 = city[id].y[2];
int x3 = city[id].x[3], y3 = city[id].y[3];
double dis1 = dist(x1, y1, x2, y2), dis2 = dist(x1, y1, x3, y3), dis3 = dist(x3, y3, x2, y2);
int q = main_p(dis1, dis2, dis3);
int x4, y4;
switch(q) {
case 1:{
city[id].x[4] = x4 = x2 + x3 - x1;
city[id].y[4] = y4 = y3 + y2 - y1;
break;
}
case 2:{
city[id].x[4] = x4 = x1 + x3 - x2;
city[id].y[4] = y4 = y3 + y1 - y2;
break;
}
case 3:{
city[id].x[4] = x4 = x2 + x1 - x3;
city[id].y[4] = y4 = y1 + y2 - y3;
break;
}
}
//printf("->%d %d\n", x4, y4);
//printf("->%lf %lf %lf\n", dis1, dis2, dis3);
int tt = city[id].T;
add_edge((id-1)*4+1, (id-1)*4+2, dis1*tt); add_edge((id-1)*4+2, (id-1)*4+1, dis1*tt);
add_edge((id-1)*4+1, (id-1)*4+3, dis2*tt); add_edge((id-1)*4+3, (id-1)*4+1, dis2*tt);
add_edge((id-1)*4+3, (id-1)*4+2, dis3*tt); add_edge((id-1)*4+2, (id-1)*4+3, dis3*tt);
dis1 = dist(x1, y1, x4, y4), dis2 = dist(x2, y2, x4, y4), dis3 = dist(x3, y3, x4, y4);
//printf("->%lf %lf %lf\n", dis1, dis2, dis3);
add_edge((id-1)*4+1, (id-1)*4+4, dis1*tt); add_edge((id-1)*4+4, (id-1)*4+1, dis1*tt);
add_edge((id-1)*4+2, (id-1)*4+4, dis2*tt); add_edge((id-1)*4+4, (id-1)*4+2, dis2*tt);
add_edge((id-1)*4+3, (id-1)*4+4, dis3*tt); add_edge((id-1)*4+4, (id-1)*4+3, dis3*tt);
return;
}
inline void build_airline()
{
for(int i = 1; i <= n; i++) {
for(int j = i+1; j <= n; j++) {
for(int a = 1; a <= 4; a++) {
for(int b = 1; b <= 4; b++) {
double d1 = dist(city[i].x[a], city[i].y[a], city[j].x[b], city[j].y[b]);
//printf("->%lf %d\n", d1, t);
add_edge((i-1)*4+a, (j-1)*4+b, d1 * t); add_edge((j-1)*4+b, (i-1)*4+a, d1 * t);
}
}
}
}
return;
}
struct node{
double dis;
int pos;
inline bool operator <(const node& x)const{
return x.dis < dis;
}
};
priority_queue <node> Q;
inline void dijkstra(int k, int s)
{
memset(vis, 0, sizeof(vis));
while(!Q.empty()) Q.pop();
Q.push((node){0, s});
dis[k][s] = 0;
while(!Q.empty()) {
node top = Q.top();Q.pop();
int x = top.pos;
if(vis[x]) continue;
vis[x] = 1;
for(int i = head[x]; i; i = edge[i].next) {
int y = edge[i].to;
if(dis[k][y] - dis[k][x] - edge[i].w > 1e-2) {
dis[k][y] = dis[k][x] + edge[i].w;
if(!vis[y]) Q.push((node){dis[k][y], y});
}
}
}
}
int main() {
o = read();
while(o--) {
cnt = 0;
n = read(), t = read(), a = read(), b = read();
for(int i = 1; i <= n; i++) {
city[i].x[1] = read(), city[i].y[1] = read();
city[i].x[2] = read(), city[i].y[2] = read();
city[i].x[3] = read(), city[i].y[3] = read();
city[i].T = read();
build_highway(i);
}
build_airline();
for(int i = 1; i <= 4; i++)
for(int j = 1; j <= n*4; j++) dis[i][j] = INF+1.0;
for(int i = 1; i <= 4; i++) dijkstra(i, (a-1)*4+i);
double ans = INF + 1.0;
for(int i = 1; i <= 4; i++)
for(int j = 1; j <= 4; j++) ans = min(ans, dis[i][(b-1)*4+j]);
printf("%.1lf\n", ans);
}
return 0;
}
转圈游戏
noip2013 提高组day1 t1
火柴排队
noip2013 提高组day1 t2
难度:一般
思路:
求逆序对。
Code:
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
//Mystery_Sky
//
#define M 1001000
#define INF 0x3f3f3f3f
#define ll long long
#define Mod 99999997
#define lowbit(x) ((x) & -(x))
inline int read()
{
int x=0,f=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int n;
int a[M], b[M], c[M], d[M], q[M], val[M];
inline bool cmp1(int i, int j) {return a[i] < a[j];}
inline bool cmp2(int i, int j) {return b[i] < b[j];}
inline void add(int x, int k)
{
while(x <= n) {
val[x] += k;
x += lowbit(x);
}
}
inline ll query(int x)
{
ll res = 0;
while(x) {
res += val[x];
x -= lowbit(x);
}
return res;
}
int main() {
n = read();
for(int i = 1; i <= n; i++) a[i] = read(), c[i] = i;
for(int i = 1; i <= n; i++) b[i] = read(), d[i] = i;
sort(c+1, c+n+1, cmp1);
sort(d+1, d+n+1, cmp2);
for(int i = 1; i <= n; i++) q[c[i]] = d[i];
ll ans = 0;
for(int i = n; i >= 1; i--) {
ans += query(q[i]-1);
add(q[i], 1);
ans %= Mod;
}
printf("%lld\n", ans%Mod);
return 0;
}
货车运输
noip2013 提高组day1 t3
难度:适中
思路:
先求出最大生成树,然后将边权值赋成点权值,用lca查询即可。
Code:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
//Mystery_Sky
//
#define M 100100
#define INF 0x3f3f3f3f
#define ll long long
inline int read()
{
int x=0,f=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
struct Road{
int x, y, w;
}road[M];
struct Edge{
int next, to, w;
}edge[M];
int n, m, q, cnt, ans, t;
int head[M], f[M], fa[M][30], d[M], dis[M][30];
queue <int> Q;
inline bool cmp(Road a, Road b) {return a.w > b.w;}
inline void add_edge(int u, int v, int w) {edge[++cnt].to = v; edge[cnt].next = head[u]; edge[cnt].w = w; head[u] = cnt;}
inline int find(int x) {return x == f[x] ? x : f[x] = find(f[x]);}
inline void Kruskal()
{
for(int i = 1; i <= n; i++) f[i] = i;
sort(road+1, road+1+m, cmp);
for(int i = 1; i <= m; i++) {
int a = find(road[i].x), b = find(road[i].y);
if(a != b) {
f[a] = b;
add_edge(a, b, road[i].w); add_edge(b, a, road[i].w);
}
}
return;
}
inline void bfs(int s)
{
d[s] = 1;
dis[s][0] = INF;
Q.push(s);
while(!Q.empty()) {
int x = Q.front(); Q.pop();
for(int i = head[x]; i; i = edge[i].next) {
int y = edge[i].to;
if(d[y]) continue;
d[y] = d[x] + 1;
fa[y][0] = x;
dis[y][0] = edge[i].w;
for(int j = 1; j <= t; j++) fa[y][j] = fa[fa[y][j-1]][j-1], dis[y][j] = min(dis[y][j-1], dis[fa[y][j-1]][j-1]);
Q.push(y);
}
}
}
inline int lca(int x, int y)
{
int res = INF;
if(find(x) != find(y)) return -1;
if(d[x] > d[y]) swap(x, y);
for(int i = t; i >= 0; i--)
if(d[fa[y][i]] >= d[x]) res = min(dis[y][i], res), y = fa[y][i];
if(x == y) return res;
for(int i = t; i >= 0; i--)
if(fa[x][i] != fa[y][i]) {
res = min(dis[x][i], min(dis[y][i], res));
x = fa[x][i], y = fa[y][i];
}
return min(res, min(dis[x][0], dis[y][0]));
}
int main() {
n = read(), m = read();
t = (log(n) / log(2));
for(int i = 1; i <= m; i++) road[i].x = read(), road[i].y = read(), road[i].w = read();
Kruskal();
q = read();
for(int i = 1; i <= n; i++)
if(!d[i]) bfs(i);
for(int i = 1; i <= q; i++) {
int x = read(), y = read();
ans = lca(x, y);
printf("%d\n", ans);
}
return 0;
}
玩具谜题
noip2016 提高组day1 t1
题意:
根据信息找玩具。
难度:签到题
思路:
模拟即可。
Code:
#include <iostream>
#include <cstdio>
using namespace std;
//Mystery_Sky
//
#define INF 0x3f3f3f3f
#define ll long long
#define M 200010
inline int read()
{
int x=0,f=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
struct node{
string name;
int turn;
}man[M];
int n, m;
int main() {
// freopen("miti.in", "r", stdin);
// freopen("miti.out", "w", stdout);
n = read(), m = read();
for(int i = 1; i <= n; i++) {
man[i].turn = read();
cin >> man[i].name;
}
int id = 1;
for(int i = 1; i <= m; i++) {
int t = read(), num = read();
if(man[id].turn == 0) {
if(t == 0) {
id -= num;
if(id <= 0) id += n;
}
else {
id += num;
if(id > n) id -= n;
}
}
else {
if(t == 0) {
id += num;
if(id > n) id -= n;
}
else {
id -= num;
if(id <= 0) id += n;
}
}
}
cout << man[id].name << endl;
return 0;
}
换教室
noip2016 提高组day1 t3
难度:适中
思路:
感觉t2比这个难。。。
暴力的写法大概可以拿到72分(或者更高?)
正解是floyd+期望dp即可,时间够的话这个dp方程不难推出来。
Code:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
//Mystery_Sky
//
#define M 3000
#define INF 0x3f3f3f3f
inline int read()
{
int x=0, f=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int n, m, v, e;
int c[M], d[M], dis[500][500];
double k[M], ans, f[M][M][2];
inline void floyd()
{
for(int i = 1; i <= v; i++) dis[i][i] = 0;
for(int k = 1; k <= v; k++)
for(int i = 1; i <= v; i++)
for(int j = 1; j <= v; j++)
if(dis[i][j] > dis[i][k] + dis[k][j]) dis[i][j] = dis[i][k] + dis[k][j];
}
int main() {
n = read(), m = read(), v = read(), e = read();
for(int i = 1; i <= n; i++) c[i] = read();
for(int i = 1; i <= n; i++) d[i] = read();
for(int i = 1; i <= n; i++) scanf("%lf", &k[i]);
memset(dis, INF, sizeof(dis));
for(int i = 1; i <= e; i++) {
int u = read(), v = read(), w = read();
dis[u][v] = dis[v][u] = min(w, dis[u][v]);
}
floyd();
for(int i = 0; i <= n; i++)
for(int j = 0; j <= m; j++) f[i][j][0] = f[i][j][1] = INF+1.0;
f[1][0][0] = f[1][1][1] = 0;
for(int i = 2; i <= n; i++) {
f[i][0][0] = f[i-1][0][0] + dis[c[i-1]][c[i]];
for(int j = 1; j <= min(i, m); j++) {
f[i][j][0] = min(f[i][j][0], min(f[i-1][j][0] + dis[c[i-1]][c[i]], f[i-1][j][1] + dis[d[i-1]][c[i]] * k[i-1] + dis[c[i-1]][c[i]] * (1 - k[i-1])));
f[i][j][1] = min(f[i][j][1], min(f[i-1][j-1][0] + dis[c[i-1]][d[i]] * k[i] + dis[c[i-1]][c[i]] * (1 - k[i]), f[i-1][j-1][1] + dis[d[i-1]][d[i]] * k[i-1] * k[i] + dis[d[i-1]][c[i]] * k[i-1] * (1 - k[i]) + dis[c[i-1]][d[i]] * (1 - k[i-1]) * k[i] + dis[c[i-1]][c[i]] * (1 - k[i-1]) * (1 - k[i])));
}
}
double ans = INF + 1.0;
for(int i = 0; i <= m; i++) ans = min(ans, min(f[n][i][0], f[n][i][1]));
printf("%.2lf\n", ans);
return 0;
}
蚯蚓
noip2016 提高组day2 t2
题意:
给定n条蚯蚓,接下来的m秒钟的时间里每秒将最长的蚯蚓切成⌊px⌋和x-⌊px⌋(0<p<1),其余蚯蚓则增长p,每t秒输出一次本次切割的蚯蚓的原长,最后将所得蚯蚓从大到小排序后输出第t、2t、3t……的蚯蚓的长度。ps:切割所得的长度为0的蚯蚓也需要保留
难度:中等
思路:
[60分做法]:
根据数据范围,有12个点的q为0,即蚯蚓不会增长,那么只需用优先队列将所有的蚯蚓存入,每次取出队首,切割完再放回即可。
Code:
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
//Mystery_Sky
//预计->60
#define M 200010
#define INF 0x3f3f3f3f
#define ll long long
inline int read()
{
int x=0,f=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
return x*f;
}
int n, m, q, u, v, t;
double p;
int a[M];
struct node{
int l;
inline bool operator <(const node& x)const{
return x.l > l;
}
};
priority_queue <node> Q1;
priority_queue <node> Q2;
vector <int> ans_1;
vector <int> change;
int main() {
n = read(), m = read(), q = read(), u = read(), v = read(), t = read();
p = u * 1.0 / v;
for(int i = 1; i <= n; i++) a[i] = read(), Q1.push((node){a[i]});
for(int i = 1; i <= m; i++) {
node top = Q1.top();Q1.pop();
int x1 = (int)(top.l * p);
int x2 = top.l - x1;
Q1.push((node){x1}); Q1.push((node){x2});
if(i % t == 0) ans_1.push_back(top.l);
}
int len = ans_1.size();
for(int i = 0; i < len; i++) printf("%d ", ans_1[i]);
putchar(10);
int tot = 0;
while(!Q1.empty()) {
tot++;
node top = Q1.top(); Q1.pop();
if(tot % t == 0) {
printf("%d ", top.l);
}
}
return 0;
}
[70分做法]:
考试的时候想出来的一种做法。建立两个优先队列,结构体中增加c和b,用c表示该条蚯蚓上次被切割是在第c秒,b = l - c,然后以b的大小为关键字将所有的蚯蚓排列。先将所有蚯蚓存入Q1,每次切完后,将长的一条存入Q1,短的一条存入Q2,然后每次取出Q1和Q2队首中b的值更大的一条去切割。对于第i秒切割的蚯蚓,其当前长度则为l + (i-1) * q - c * q, 而切割完后,每条蚯蚓当前长度则为l + (i - c) * q。
Code:
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
//Mystery_Sky
//->70
#define M 200010
#define INF 0x3f3f3f3f
#define ll long long
inline int read()
{
int x=0,f=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
return x*f;
}
int n, m, q, u, v, t;
double p;
int a[M];
struct node{
int l;
int c;
int b;
inline bool operator <(const node& x)const{
return x.b > b;
}
};
priority_queue <node> Q1;
priority_queue <node> Q2;
priority_queue <int> Q;
vector <int> ans_1;
int main() {
n = read(), m = read(), q = read(), u = read(), v = read(), t = read();
p = u * 1.0 / v;
for(int i = 1; i <= n; i++) a[i] = read(), Q1.push((node){a[i], 0, a[i]});
for(int i = 1; i <= m; i++) {
node top_1;
if(!Q1.empty()) top_1 = Q1.top(), Q1.pop();
node top_2;
top_2.l = -1;
if(!Q2.empty()) top_2 = Q2.top(), Q2.pop();
node top;
if(top_2.l != -1 && top_1.b >= top_2.b) Q2.push(top_2), top = top_1;
else if(top_2.l == -1) top = top_1;
else Q1.push(top_1), top = top_2;
int len = top.l - top.c + (i - 1) * q;
int x1 = (int) (len * p);
int x2 = len - x1;
if(x1 < x2) swap(x1, x2);
Q1.push((node){x1, i * q, x1-i*q});
Q2.push((node){x2, i * q, x2-i*q});
if(i % t == 0) ans_1.push_back(len);
}
int len = ans_1.size();
for(int i = 0; i < len; i++) printf("%d ", ans_1[i]);
putchar(10);
while(!Q2.empty()) {
node top = Q2.top(); Q2.pop();
Q1.push(top);
}
int tot = 0;
while(!Q1.empty()) {
tot++;
node top = Q1.top(); Q1.pop();
if(tot % t == 0) {
printf("%d ", top.l - top.c + m * q);
}
}
return 0;
}
(话说这样建两个优先队列似乎没有什么意义)
[正解]:
我们建立三个队列,嗯,就是普通的队列。先将所有的蚯蚓从大到小排序后放入Q[0],然后每次切割后将其中一条放入Q[1],另外一条放入Q[2],由于切割时间的先后导致在同一个队列中先入队的蚯蚓长度总会大于后入队的,所以这三条队列本身就是单调的,每次切的时候取三个队列的队首中最大的一个切割即可。对于增长的长度q,暴力计算显然会t,我们可以增加变量add,表示已增长的长度,每次切割时先将蚯蚓长度加上add,切完后,将两段的长度减去(add + q),再入队,add加上q即可。
Code:
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
//Mystery_Sky
//
#define M 200010
#define INF 0x3f3f3f3f
inline int read()
{
int x=0, f=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
queue <int> Q[3];
vector <int> ans_1;
int n, m, q, u, v, t, add;
double p;
int a[M];
inline bool cmp(int a, int b){return a > b;}
inline int big()
{
int res = -1;
for(int i = 0; i <= 2; i++) if(!Q[i].empty() && ((res == -1) || (Q[i].front() > Q[res].front()))) res = i;
return res;
}
int main() {
n = read(), m = read(), q = read(), u = read(), v = read(), t = read();
p = u * 1.0 / v;
for(int i = 1; i <= n; i++) a[i] = read();
sort(a+1, a+n+1, cmp);
for(int i = 1; i <= n; i++) Q[0].push(a[i]);
for(int i = 1; i <= m; i++) {
int top = big();
int x = Q[top].front() + add; Q[top].pop();
int x1 = (int)(x * p);
int x2 = x - x1;
x1 -= (add + q), x2 -= (add + q);
Q[1].push(x1), Q[2].push(x2);
if(i % t == 0) ans_1.push_back(x);
add += q;
}
for(int i = 0, len = ans_1.size(); i < len; i++) printf("%d ", ans_1[i]);
putchar(10);
for(int i = 1; i <= n+m; i++) {
int top = big();
int x = Q[top].front(); Q[top].pop();
if(i % t == 0) printf("%d ", x + add);
}
return 0;
}