咕了好几天的题解,今天总算补上了。
T1 fish
题意描述
河边有⼀块鱼塘,鱼塘中有 (n) 条鱼,鱼塘边有 (m) 个摸鱼⼈。我们对这个鱼塘建⽴坐标系,已知所有鱼的坐标 ((x_1,y_1),(x_2,y_2)...(x_n,y_n)) 和所有摸鱼⼈的坐标 (a_1,a_2..a_n) ( (a_i) 代表第 (i) 个摸鱼⼈在坐标系中的 ((a_i, 0)) 这⼀点上)。摸鱼⼈只能摸到与他的距离不超过 (l) 的范围内的鱼。 请求出每个摸鱼⼈能摸到的鱼的数量。 注意的是,在这⾥距离定义为曼哈顿距离,即⼀条鱼 $(x, y) $和⼀个摸鱼⼈ ((a, 0)) 的距离为 (d = ∣a − x∣ + y) 。 鱼的坐标可以重叠,即⼀个点上可能存在多条鱼。问你每个摸鱼人能摸到的鱼的数量。
(n,mleq 2 imes 10^5,1leq x_i,y_i,a_i,lleq 10^9)
solution
考试的时候极值设小了,挂了 (50) 分,直接倒数了。
鱼会被摸鱼人抓到 要满足, (∣a − x∣ + yleq l) 其中 (d,y) 都是确定的,把这两项移到左边变成。
(|a-x| leq l-y) ,然后你会发现 合法的 (x) 对应的是一段区间, ([a-(l-y),a+(l-y)]) 。
可以先对摸鱼人按 (x_i) 排一下序,二分出左右端点在差分一下就行。
复杂度 (O(nlogn))
Code(写的难看了点)
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
const int N = 2e5+10;
const int inf = 1e13;
int n,m,maxl,d[N],ans[N],pos[N];
struct fish
{
int x,y;
}e[N];
struct people
{
int x,id;
}q[N];
inline int read()
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
bool comp(people a,people b)
{
return a.x < b.x;
}
int find_L(int val)
{
int L = 0, R = m+1, res = 0;
while(L <= R)
{
int mid = (L + R)>>1;
if(q[mid].x <= val)
{
res = mid;
L = mid + 1;
}
else R = mid - 1;
}
return res;
}
int find_R(int val)
{
int L = 0, R = m+1, res = 0;
while(L <= R)
{
int mid = (L + R)>>1;
if(q[mid].x >= val)
{
res = mid;
R = mid - 1;
}
else L = mid + 1;
}
return res;
}
signed main()
{
freopen("fish.in","r",stdin);
freopen("fish.out","w",stdout);
n = read(); m = read(); maxl = read();
for(int i = 1; i <= n; i++)
{
e[i].x = read();
e[i].y = read();
}
for(int i = 1; i <= m; i++)
{
q[i].x = read();
q[i].id = i;
}
sort(q+1,q+m+1,comp); q[0].x = -inf; q[m+1].x = inf;
for(int i = 1; i <= m; i++) pos[q[i].id] = i;
for(int i = 1; i <= n; i++)
{
if(e[i].y > maxl) continue;
int l = find_L(e[i].x - (maxl - e[i].y)-1);
int r = find_R(e[i].x + (maxl - e[i].y)+1);
d[l+1]++; d[r]--;
}
for(int i = 1; i <= m; i++) d[i] = d[i-1] + d[i];
for(int i = 1; i <= m; i++) ans[q[i].id] = d[i];
for(int i = 1; i <= m; i++) printf("%lld
",ans[i]);
fclose(stdin); fclose(stdout);
return 0;
}
T2 package
题⽬描述 :
⼜是⼀年双⼗⼀,华科的每个快递点都炸了仓库,排队领快递的场景⼗分壮观。现在⼀群学⽣开展了⼀ 项代领快递的业务,他们骑着电动⾃⾏车帮同学们领取他们的快递。 ⼀共有 (T) 件快递需要领取,每件快递都有两个属性重量 (W_i) 和⼤⼩ (S_i) 。⽽他们的电动车也分为两 类:功率不太⾼的和不太⼤的。对于每台功率不太⾼的电动车,只能装重量严格⼩于 (X_i) 的快递,但对快递的⼤⼩不做限制;对于每台不太⼤的电动车,只能装⼤⼩严格⼩于 (Y_i) 的快递,但对快递的重量不 做限制。每取 (1) 件快递需要消耗 (1) 分钟的时间,所有电单车可以同时取快递。 已知他们⼀共有 (A) 台功率不太⾼的电动车和 (B) 台不太⼤的电动车,请求出领取完所有快递所需的最短 时间(如果⽆法领取完所有的快递,输出 -1 )。
(Tleq 10^6,a,bleq 50000, x_i,y_i,W_i,S_ileq 2 imes 10^9)
solution
比较神仙的贪心题。
最后的答案与装的最多的电动车的快递数有关。显然答案有单调性。考虑二分答案 (t).
接下来考虑如何验证。
对于每个功率不太高的电动车,对大小没有限制,所以我们要让他在他能装的范围里面选大小比较大的装。
先对每个 (x_i) 按从小到大排序,每个快递按 (w) 为第一关键字, (s) 为第二关键字排序。
显然随着 (x_i) 的递增, 可选择的快递也是扩大的,把每个快递按 (s) 为关键字放入堆中,取前 (t) 大的放。。
对于不能放的快递,则用不太大的电车来放。先对每个 (y_i) 按从大到小排一遍序。
如果当前堆顶比 (y_i) 要大,说明当前堆顶的快递已经放不下了,直接 (return 0) .
最后如果堆不为空,则表示当前的快递没有装完,也是 (return 0)
复杂度 (O(nlog^2n))
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 1e6+10;
int n,m,T,ans;
int x[50010],y[50010];
struct node
{
int s,w;
}e[N];
inline int read()
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
bool comp(node a,node b)
{
if(a.w == b.w) return a.s < b.s;
return a.w < b.w;
}
bool judge(int mid)
{
priority_queue<int> q;
int now = 1;
for(int i = 1; i <= n; i++)
{
while(now <= T && e[now].w < x[i]) //把能选的放进堆里面
{
q.push(e[now].s);
now++;
}
for(int j = 1; j <= mid && !q.empty(); j++) q.pop();//选前t的取,同时pop出来表示这个快递已经被选了
}
while(now <= T)//把剩下的放进去
{
q.push(e[now].s);
now++;
}
for(int i = m; i >= 1; i--)
{
if(!q.empty() && q.top() >= y[i]) return 0;
for(int j = 1; j <= mid && !q.empty(); j++) q.pop();
}
if(q.empty()) return 1;
return 0;
}
int main()
{
freopen("package.in","r",stdin);
freopen("package.out","w",stdout);
n = read(); m = read(); T = read();
for(int i = 1; i <= n; i++) x[i] = read();
for(int i = 1; i <= m; i++) y[i] = read();
sort(x+1,x+n+1); sort(y+1,y+m+1);
for(int i = 1; i <= T; i++)
{
e[i].w = read();
e[i].s = read();
}
sort(e+1,e+T+1,comp);
int L = 1, R = T;
while(L <= R)
{
int mid = (L + R)>>1;
if(judge(mid))
{
ans = mid;
R = mid - 1;
}
else L = mid + 1;
}
if(ans == 0) printf("%d
",-1);
else printf("%d
",ans);
fclose(stdin); fclose(stdout);
return 0;
}
T3 arrange
题意描述
给你一个长度为 (n) 的 序列 (a_i) ,对于 ({A_n}) 的一个排列 (f_n) 如果 (displaystylesum_{i=2}^{n} |f_i-f_{i-1}|leq L) ,则称他是符合要求的。求满足条件的排列个数。 (nleq 100,a_ileq 1000)
solution
神仙 (dp) 题,不会。
正解,咕咕咕。。。
T4 game
题意描述
最近,小P迷上了一款叫做2048的游戏。这块游戏在一个 (n imes n) 的棋盘中进行,棋盘的每个格子中可能有一个形如 (2^k(k∈N∗))的数,也可能是空的。游戏规则介绍如下:
1.游戏开始时棋盘内将会生成两个数字,生成的数字仅可能为2或4;
2.每次操作,玩家可以选择上、下、左、右四个方向进行平移;
3.以向上平移为例,从上往下考虑每个不为空的格子,若上方与之相邻的格子为空,则将该格子上的数字移动至相邻格子。在一次位移中,每个数字会进行多次移动直到不能移动为止。
4.以向上平移为例,从上往下考虑每个不为空的格子,若上方与之相邻的数字恰好与其相等,则两个数字可以合并,新生成的数字为原来两个数之和。在一次合并中,每个数字只能与其它数合并一次,可以同时合并多对数字,但不能连续合并;
5.每次操作由位移+合并+位移组成,若操作后棋盘局面发生变化,则该操作为有效操作,其有效得分为合并过程中所有新生成的数字之和;
6.在每次操作后,棋盘内都会新生成一个数字2或4,数字只会在空格子处生成;
7.当棋盘被数字填满,玩家无法进行任何有效操作时,游戏结束,游戏总得分为所有操作的有效得分之和。
为了降低难度,小P对2048游戏进行了一些改动。在游戏开始前,小P会告诉你棋盘的初始状态,并给你若干次操作。每次操作由方向变量、位置参数和一个数字组成,方向变量代表你在本次操作中的移动方向,给定的数字为本次操作之后将会生成的数字的大小,而位置参数将决定生成数字的位置。若位置参数为K,操作后棋盘中空格子的数量为 (r),则生成数字的位置从上到下、从左到右第 ((1+K mod r))个空格子。如果每次操作为无效操作,则游戏结束,而当所有操作都完成后,游戏同样结束。(注意:改动后,游戏结束时棋盘不一定被数字填满。)
现在小P问你,在游戏结束前你一共进行了多少次有效操作,最后你的游戏总得分是多少
solution
恶心人的大模拟,没什么好说的,就是要注意各种细节。
Code
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
int n,m,cnt,ans,x,y,w,di,ki,vi,flag;
int e[20][20];
struct node
{
int x,y;
}q[10010];
inline int read()
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
bool comp(node a,node b)
{
if(a.x == b.x) return a.y < b.y;
return a.x < b.x;
}
bool gameover()
{
for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) if(!e[i][j]) return 0;
for(int i = 1; i < n; i++)
{
for(int j = 1; j <= n; j++)
{
if(e[i][j] == e[i+1][j]) return 0;
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j < n; j++)
{
if(e[i][j] == e[i][j+1]) return 0;
}
}
return 1;
}
void up()
{
for(int j = 1; j <= n; j++)
{
for(int i = 1; i <= n; i++)
{
int tmp = e[i][j], r = i;
if(tmp == 0) continue;
while(r-1 >= 1 && !e[r-1][j]) r--;
if(r != i) flag = 1;
e[i][j] = 0; e[r][j] = tmp;
}
}
for(int j = 1; j <= n; j++)
{
for(int i = 1; i <= n; i++)
{
if(e[i][j] == e[i-1][j] && e[i][j] != 0)
{
flag = 1;
e[i-1][j] *= 2;
e[i][j] = 0;
ans += e[i-1][j];
}
}
}
for(int j = 1; j <= n; j++)
{
for(int i = 1; i <= n; i++)
{
int tmp = e[i][j], r = i;
if(tmp == 0) continue;
while(r-1 >= 1 && !e[r-1][j]) r--;
if(r != i) flag = 1;
e[i][j] = 0; e[r][j] = tmp;
}
}
}
void down()
{
for(int j = 1; j <= n; j++)
{
for(int i = n; i >= 1; i--)
{
int tmp = e[i][j], r = i;
if(tmp == 0) continue;
while(r+1 <= n && !e[r+1][j]) r++;
if(r != i) flag = 1;
e[i][j] = 0; e[r][j] = tmp;
}
}
for(int j = 1; j <= n; j++)
{
for(int i = n; i >= 1; i--)
{
if(e[i][j] == e[i+1][j] && e[i][j] != 0)
{
flag = 1;
e[i+1][j] *= 2;
e[i][j] = 0;
ans += e[i+1][j];
}
}
}
for(int j = 1; j <= n; j++)
{
for(int i = n; i >= 1; i--)
{
int tmp = e[i][j], r = i;
if(tmp == 0) continue;
while(r+1 <= n && !e[r+1][j]) r++;
if(r != i) flag = 1;
e[i][j] = 0; e[r][j] = tmp;
}
}
}
void left()
{
// cout<<res<<"rejp"<<endl;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
int tmp = e[i][j], r = j;
if(tmp == 0) continue;
while(r-1 >= 1 && !e[i][r-1]) r--;
if(r != j) flag = 1;
e[i][j] = 0; e[i][r] = tmp;
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
if(e[i][j] == e[i][j-1] && e[i][j] != 0)
{
flag = 1;
e[i][j-1] *= 2;
e[i][j] = 0;
ans += e[i][j-1];
}
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
int tmp = e[i][j], r = j;
if(tmp == 0) continue;
while(r-1 >= 1 && !e[i][r-1]) r--;
if(r != j) flag = 1;
e[i][j] = 0; e[i][r] = tmp;
}
}
}
void right()
{
for(int i = 1; i <= n; i++)
{
for(int j = n; j >= 1; j--)
{
int tmp = e[i][j], r = j;
if(tmp == 0) continue;
while(r+1 <= n && !e[i][r+1]) r++;
if(r != j) flag = 1;
e[i][j] = 0; e[i][r] = tmp;
}
}
for(int i = 1; i <= n; i++)
{
for(int j = n; j >= 1; j--)
{
if(e[i][j] == e[i][j+1] && e[i][j] != 0)
{
flag = 1;
e[i][j+1] *= 2;
e[i][j] = 0;
ans += e[i][j+1];
}
}
}
for(int i = 1; i <= n; i++)
{
for(int j = n; j >= 1; j--)
{
int tmp = e[i][j], r = j;
if(tmp == 0) continue;
while(r+1 <= n && !e[i][r+1]) r++;
if(r != j) flag = 1;
e[i][j] = 0; e[i][r] = tmp;
}
}
}
void remove(int ki,int vi)
{
int num = 0;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
if(!e[i][j])
{
q[++num].x = i;
q[num].y = j;
}
}
}
sort(q+1,q+num+1,comp);
if(num == 0) return;
int tmp = (ki % num) + 1;
e[q[tmp].x][q[tmp].y] = vi;
}
signed main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
n = read(); m = read();
x = read(); y = read(); w = read(); e[x][y] = w;
x = read(); y = read(); w = read(); e[x][y] = w;
for(int i = 1; i <= m; i++)
{
di = read(); ki = read(); vi = read(); flag = 0;
if(gameover())
{
cnt = i-1;
printf("%lld
%lld
",cnt,ans);
return 0;
}
if(di == 0) up();
if(di == 1) down();
if(di == 2) left();
if(di == 3) right();
remove(ki,vi);
if(flag == 0)
{
cnt = i-1;
printf("%lld
%lld
",cnt,ans);
return 0;
}
}
cnt = m;
printf("%lld
",cnt);
printf("%lld
",ans);
fclose(stdin); fclose(stdout);
return 0;
}