The 2021 ICPC Asia Regionals Online Contest (I)

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

字典树.


这两天有时间接着做吧…

猜你喜欢

转载自blog.csdn.net/m0_50435987/article/details/121238506