The 2021 ICPC Asia Regionals Online Contest (I)
写了一晚上,日…
文章目录
一、 A Busiest Computing Nodes
题意:有k个计算节点和n个询问,后0-n-1行每个询问给定一个节点被占用的起始时间和占用时间。最好占用第i%k个节点,若该节点被占用,则依次往后寻找节点占用,(i%k),(i+1)%k,(i+2)%k… 如果都被占用则跳过此次查询。
思路:线段树+二分.
维护区间上的最小值,即结束占用的最早时间。如果右子区间上有则二分查找右子区间,没有则二分查找左子区间.
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
typedef long long ll;
const int N = 100010;
int k,n;
int p[N]; // 记录占用情况.
struct Tree{
int l,r;
int v; // 记录区间内的最小结束时间:起初均为 0
}tr[N*4];
void pushup(Tree &rt,Tree &l,Tree &r){
rt.v = min(l.v,r.v);
}
void pushup(int u){
pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r){
tr[u] = {l,r};
if(l==r){
tr[u] = {l,l,0};
return;
}
int mid = (l + r) >> 1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
int query(int u,int l,int r){
if(l<=tr[u].l && tr[u].r<=r) return tr[u].v; //返回v.
int mid = (tr[u].l + tr[u].r) >> 1;
int ans = 1e9;
if(l<=mid) ans = min(ans,query(u<<1,l,r));
if(r>mid) ans = min(ans,query(u<<1|1,l,r));
return ans;
}
void modify(int u,int x,int val){
if(tr[u].l==x && tr[u].r==x){
tr[u].v = val;
return;
}
int mid = (tr[u].l + tr[u].r) >> 1;
if(x<=mid){
modify(u<<1,x,val);
}else{
modify(u<<1|1,x,val);
}
pushup(u);
}
signed main()
{
if(scanf("%lld%lld",&k,&n)){
build(1,1,k);
// for(int i=1;i<=5;i++){
// cout << i << " " << tr[i].l << " " << tr[i].r << " " << tr[i].v << endl;
// }
// 到达时间是否需要排序 不需要 The requests are given in non-decreasing order of arrival time.
for(int i=0;i<n;i++){
int st_time,ocupy_time;
if(scanf("%lld%lld",&st_time,&ocupy_time)){
if(query(1,i%k+1,k)<=st_time){ // 右子区间有
int l = i%k + 1, r = k;
while(l<r){
int mid = (l + r) >> 1;
if(query(1,l,mid)<=st_time) r = mid;
else l = mid + 1;
}
modify(1,l,st_time+ocupy_time);
p[l] ++;
}else if(i%k){ // 左子区间有
if(query(1,1,i%k)<=st_time){
int l = 1, r = i%k;
while(l<r){
int mid = (l + r) >> 1;
if(query(1,l,mid)<=st_time) r = mid;
else l = mid + 1;
}
modify(1,l,st_time+ocupy_time);
p[l] ++;
}
}
}
}
int mx = 0;
for(int i=1; i<=k; i++) mx = max(mx,p[i]);
int cnt = 0;
for(int i=1; i<=k; i++) if(mx==p[i]) cnt++;
for(int i=1; i<=k; i++)
if(mx==p[i]) {
printf("%lld",i-1);
if(--cnt) printf(" ");
}
}
return 0;
}
二、D Edge of Taixuan
这题饶了半天,好长的题.
题意:给定一个无向图,可能存在重边,每条边存在一个边权,问在保证图连通性的前提下最长可以删除的边的长度为多少?如果整个图不连通,则输出“Gotta prepare a lesson”.
思路:想了挺久,刚开始以为是求最小生成数。可是边数太jb多了,这要全存下来最坏的情况已经超时了.
问题每次给出的是区间的 l 和 r ,在区间上作改变—>线段树.怎么写线段树成了关键。
思考这样一种思路:1. 将查询的信息储存并按照权值从大到小进行排序,在进行遍历时每次都通过线段树进行区间修改。 2. 将边权看成点权。将i和i+1之间边的权重看成点i的权重。这样在修改时相当于对区间 [l,r-1] 每个点进行修改;在查询时相当于单点查询。
这样的最小生成树的权值就是前n-1个结点的权重。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 100010;
struct Edge{
int l,r;
int w;
bool operator <(const Edge &W) const
{
return w>W.w;
}
}e[N];
struct Tree{
int l,r;
int m,add; //add为懒标记.
}tr[N * 4];
void pushup(int u){
tr[u].m = min(tr[u<<1].m,tr[u<<1|1].m);
}
void pushdown(int u){
Tree &rt = tr[u];
Tree &l = tr[u<<1];
Tree &r = tr[u<<1|1];
if(rt.add){
l.add += rt.add, l.m = rt.m;
r.add += rt.add, r.m = rt.m;
rt.add = 0;
}
}
void build(int u,int l,int r){
tr[u] = {l,r,0,0};
if(l==r){
tr[u] = {l,l,0,0};
return;
}
int mid = (l+r)>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
int query(int u,int l,int r){
if(tr[u].l>=l && tr[u].r<=r) return tr[u].m;
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
int ans = 1e9;
if(l<=mid) ans = min(ans,query(u<<1,l,r));
if(r>mid) ans = min(ans,query(u<<1|1,l,r));
return ans;
}
void modify(int u,int l,int r,int val){
if(tr[u].l>=l && tr[u].r<=r){
tr[u].m = val;
tr[u].add = val;
return;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if(l<=mid){
modify(u<<1,l,r,val);
}
if(r>mid){
modify(u<<1|1,l,r,val);
}
pushup(u);
}
int main()
{
int t;
scanf("%d",&t);
for(int k=1;k<=t;k++){
int n,m;
scanf("%d%d",&n,&m);
sizeof(tr,0,sizeof tr);
build(1,1,n);
for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].l,&e[i].r,&e[i].w);
sort(e+1,e+m+1);
ll sum = 0;
for(int i=1;i<=m;i++){
int l,r,w;
l = e[i].l, r = e[i].r, w = e[i].w;
modify(1,l,r-1,w);
sum += ((ll)r - l + 1) * (ll)w * ((ll)(r-l)) / 2;
}
// cout << sum << endl;
// for(int i=1;i<=5;i++){
// cout << "节点" << i << ":" << tr[i].l << " " << tr[i].r << " " << tr[i].m << " " << tr[i].add << endl;
// }
bool f = 1;
for(int i=1;i<n;i++){
int t = query(1,i,i);
// cout << "结点" << i << " = " << t << endl;
if(!t){
f = 0;
break;
}else{
sum -= (ll)t;
}
}
printf("Case #%d: ",k);
if(f) printf("%lld",sum);
else printf("Gotta prepare a lesson");
if(k!=t) printf("\n");
}
return 0;
}
三、F Land Overseer
没啥好说的,注意b<=r 时的情况就行.
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
// 注意,如果b<=R时,直接从(0,0)走到(2a-R,0)即可。
int main()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++){
int a,b,r;
scanf("%d%d%d",&a,&b,&r);
double d = 0.0;
if(b<=r){
d = 2.0*a - r;
}else{
d = sqrt((double)a*a + ((double)b - r)*((double)b - r)) * 2.0 - (double)r;
}
printf("Case #%d: ",i);
printf("%.2lf",d);
if(i!=t) printf("\n");
}
return 0;
}
四、I Neiborhood Search
文本处理.
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <string>
#include <cmath>
using namespace std;
typedef long long ll;
int a[100010];
void solve(){
int i = 0;
int s = 0;
string ss;
getline(cin,ss);
for(int j=0;j<ss.size();j++){
if(ss[j] == ' '){
a[i++] = s;
s = 0;
continue;
}
s = s * 10 + (ss[j] - '0');
}
sort(a,a+i);
int pos,r;
scanf("%d%d",&pos,&r);
for(int j=i-1;j>=0;j--){
if(abs(a[j]-pos)<=r) printf("%d ",a[j]);
}
}
int main()
{
int t = 1;
// scanf("%d",&t);
while(t--){
solve();
}
return 0;
}
五、K Segment Routing
题目比较难读懂,大致模拟一下即可。
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 1e5 + 10, M = 2 * N;
int n, m, k;
int a[N];
vector<int> p[N];
int main(){
int T;
cin >> T;
for (int k = 1; k <= T; k ++ ) {
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) {
p[i].clear();
int cnt, v;
cin >> cnt;
for (int j = 1; j <= cnt; j ++ ) {
cin >> v;
p[i].push_back(v);
}
}
printf("Case #%d: \n", k);
for (int i = 1; i <= m; i ++ ) {
bool ok = false;
int u, v, cnt, vis;
cin >> u >> cnt;
vis = u;
for (int j = 1; j <= cnt; j ++ ) {
cin >> v;
if (v > p[vis].size() || !v) ok = true;
if (ok) continue;
vis = p[vis][v - 1];
}
if (ok) cout << "Packet Loss" << endl;
else cout << vis << endl;
}
}
return 0;
}
六、H Mesh Analysis
模拟
/*
H Mesh Analysis
题意:第一行给定 n和 m 1≤N≤10000,1≤M≤21000,代表 n个点,m个element(三角形或线段)
第2-n+1行,给定点的id和点的三维坐标(x,y,z)
第n+2-n+2+m-1行,给定element的id和类型(102代表线段,203代表三角形),每行是element的顶点或端点坐标.
下一行输入L,有L个询问
最后 L 行每行输入一个id,输出该点周围的点的id和所在element 的id
(感觉点的坐标没有任何用)
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;
struct Node1{
int a,b;
int id;
bool operator< (const Node1 &W)const
{
return id<W.id;
}
};
struct Node2{
int a,b,c;
int id;
bool operator< (const Node2 &w)const
{
return id<w.id;
}
};
map<Node1,int> mp1;
map<Node2,int> mp2;
map<int,int> mp3;
set<int> s1,s2;
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
double x,y,z;
int id;
int n,m;
cin >> n >> m;
for(int i=1;i<=n;i++){
cin >> id >> x >> y >> z;
mp3[id] = 1;
}
int t;
int a,b,c;
for(int i=1;i<=m;i++){
cin >> id >> t;
if(t==203){
cin >> a >> b >> c;
mp2[{a,b,c,id}] = 1;
}else{
cin >> a >> b;
mp1[{a,b,id}] = 1;
}
}
// 离线还是在线?
int q;
cin >> q;
for(int k=1;k<=q;k++){
cin >> a;
s1.clear();
s2.clear();
for(auto x:mp1){
if(x.first.a==a || x.first.b==a){
s1.insert(x.first.a);
s1.insert(x.first.b);
s2.insert(x.first.id);
}
}
for(auto x:mp2){
if(x.first.a==a || x.first.b==a || x.first.c==a){
s1.insert(x.first.a);
s1.insert(x.first.b);
s1.insert(x.first.c);
s2.insert(x.first.id);
}
}
cout << a << endl;
cout << "[" ;
int sz = s1.size();
int i = 0;
for(auto x:s1){
i++;
if(x==a) continue;
cout << x;
if(i!=sz) cout << ",";
}
cout << "]" << endl;
cout << "[" ;
sz = s2.size();
i = 0;
for(auto x:s2){
i++;
cout << x;
if(i!=sz) cout << ",";
}
cout << "]";
if(k!=q) cout << "\n";
}
return 0;
}
七、B Convex Polygon
二维几何:判断所给的点是否为凸多边形.并按顺时针顺序输出点(从最接近(0,0)的点开始输出)
若为否(三点共线或不为凸多边形)输出"ERROR".
题目实际上没有所谓的“非法”输入。
题目要判断是输入的所有点能否构成一个凸多边形,并且任意 3点不共线。
先判断点数 ≥3,然后O(n^3)暴力判断三点共线,最后求凸包判断凸包上的点是否等于总点数即可.
经典凸包,经典不会.
做出来了,感动中国,含泪来更新题解了
/*
B Convex Polygon
二维几何:判断所给的点是否为凸多边形.并按顺时针顺序输出点(从最接近(0,0)的点开始输出)
若为否(三点共线或不为凸多边形)输出"ERROR".
*/
/*
题目实际上没有所谓的 “非法 ”输入。
题目要判断是输入的所有点能否构成一个凸多边形,并且任意 3点不共线。
先判断点数 ≥3,然后O(n^3)暴力判断三点共线,最后求凸包判断凸包上的点是否等于总点数即可.
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 110;
const double eps = 1e-7;
struct Point {
int x, y;
Point(int x = 0,int y = 0):x(x),y(y){}
};
typedef Point Vector;
Point lst[maxn];
int stk[maxn], top;
Vector operator - (Point A, Point B){
return Vector(A.x-B.x, A.y-B.y);
}
int sgn(int x){
if(x == 0)
return 0;
if(x < 0)
return -1;
return 1;
}
int Cross(Vector v0, Vector v1) {
return v0.x*v1.y - v1.x*v0.y;
}
int Dis(Point p1, Point p2) { //计算 p1p2的 距离的平方
return (p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y);
}
bool cmp(Point p1, Point p2){ //极角排序函数 ,角度相同则距离小的在前面
int tmp = sgn(Cross(p1 - lst[0], p2 - lst[0]));
if(tmp > 0)
return true;
if(tmp == 0 && Dis(lst[0], p1) < Dis(lst[0], p2))
return true;
return false;
}
void Graham(int n) { // n为点的个数. 下标为 0 - n-1
int k = 0;
Point p0;
p0.x = lst[0].x;
p0.y = lst[0].y;
for(int i = 1; i < n; ++i) {
if( (p0.y > lst[i].y) || ((p0.y == lst[i].y) && (p0.x > lst[i].x)) ) {
p0.x = lst[i].x;
p0.y = lst[i].y;
k = i;
}
}
lst[k] = lst[0];
lst[0] = p0;
sort(lst + 1, lst + n, cmp);
stk[0] = 0;
stk[1] = 1;
top = 2;
for(int i = 2; i < n; ++i) {
while(top > 1 && Cross(lst[stk[top - 1]] - lst[stk[top - 2]], lst[i] - lst[stk[top - 2]]) <= 0)
--top;
stk[top] = i;
++top;
}
return;
}
//typedef Point Vector;
//Vector operator - (Point A, Point B){
// return Vector(A.x-B.x, A.y-B.y);
//}
//bool operator < (const Point& a, const Point& b){
// if(a.x == b.x)
// return a.y < b.y;
// return a.x < b.x;
//}
//int Cross(Vector v0, Vector v1) {
// return v0.x*v1.y - v1.x*v0.y;
//}
//计算凸包,输入点数组为 p,个数为 n, 输出点数组为 ch。函数返回凸包顶点数
//如果不希望凸包的边上有输入点,则把两个 <= 改为 <
//在精度要求高时建议用dcmp比较
//输入不能有重复点,函数执行完后输入点的顺序被破坏
//Point lst[maxn];
//Point stk[maxn], top;
//int ConvexHull(int n) {
// sort(lst, lst+n);
// int m = 0;
// for(int i = 0; i < n; ++i) {
// while(m > 1 && Cross(stk[m-1] - stk[m-2], lst[i] - stk[m-2]) < 0) {
// m--;
// }
// stk[m++] = lst[i];
// }
// int k = m;
// for(int i = n-2; i>= 0; --i) {
// while(m > k && Cross(stk[m-1] - stk[m-2], lst[i] - stk[m-2]) < 0) {
// m--;
// }
// stk[m++] = lst[i];
// }
// if(n > 1)
// --m;
// return m;
//}
signed main()
{
int x,y;
int k = 0;
while(~scanf("%d,%d,",&x,&y)){
lst[k++] = {x,y};
}
bool f = 1;
if(k<3) f = 0;
else{
for(int i=0;i<k;i++){
if(!f) break;
for(int j=i+1;j<k;j++){
if(!f) break;
for(int p=j+1;p<k;p++){
if(Cross(lst[i]-lst[j],lst[i]-lst[p])==0){
f = 0;
break;
}
}
}
}
if(f){
Graham(k);
if(k!=top) f = 0;
}
}
if(f){
int id = -1;
int mi = 10000000.00; //距离的平方.
Point w;
w.x = 0,w.y = 0;
for(int i=0;i<k;i++){
if(Dis(lst[i],w)<mi){
id = i;
mi = Dis(lst[i],w);
}
}
printf("%d,%d,",lst[id].x,lst[id].y);
int o = 1;
// 按照一个方向输出 (逆时针)
for(int i=k-1;i>id;i--){
o++;
if(o!=k) printf("%d,%d,",lst[i].x,lst[i].y);
else printf("%d,%d",lst[i].x,lst[i].y);
}
for(int i=id-1;i>=0;i--){
o++;
if(o!=k) printf("%d,%d,",lst[i].x,lst[i].y);
else printf("%d,%d",lst[i].x,lst[i].y);
}
}else{
printf("ERROR");
}
return 0;
}
// 0,0,2,0,1,1,1,2
//(P[i].x==P[j].x && P[j].x==P[p].x) || (fabs(((double)P[p].y-P[j].y)/((double)P[p].x-P[j].x))-fabs(((double)P[i].y-P[j].y)/((double)P[i].x-P[j].x))<=1e-2)
八、G Longest Prefix Matching
字典树.
这两天有时间接着做吧…