2020hdu多校第一场比赛及补题

第一场是朝鲜出题

1004 Distinct Sub-palindromes

跟榜开的这题,一开始猜是26的n次方,直接交一发wa了, 后来和队友想了半天,想到了abcabcabc,答案就是n<=3时是26的n次方, else ans = 26*25*24

1005 Fibonacci Sum

每个斐波纳契数的通项二项展开,再合并,是二项展开的套路。组合数要先预处理阶乘和阶乘逆元。

比赛时队友过的这题。

但是赛后我怎么写都T,把队友当时的AC代码交上去也T了,自闭了

1009 Leading Robots

队友写1005的时候我在想这题,想了挺久

n个机器人赛跑,每个机器人初速度为0,加速度为ai,初始位置在pi,问有多少个机器人当过第一 (第一指独一无二的最前面的那个)

容易想到下面两点

一:n*(n-1)/2的比较次数肯定是不行的,要想办法减少比较次数

二:加速度越大的最终排名一定越靠前,加速度大的人不会被加速度小的人从后方超越

考虑3个人的情况,且这3个人编号越大,起点越靠后,加速度越大,

设2号追上1号的时间 t1 ,3号追上2号的时间 t2 ,和3号追上1号的时间 t3,

若t3 < t1,即3号比2号先超过1号,ans = 2; 若t2 > t1,即2号先超过1号,然后3号超过2号,ans = 3

这里t1,t2,t3都用到了,  实际上只要用 t1和t2 或 t1和t3 就行了,

t1和t3是两个人追上同一个人分别所用的时间,感觉很难有进一步的思路,下面思考t1和t2

若t1<t2,即2号追上1号之前 3号追上了2号,3号先追上了1号,当了第一名,之后2号也无法当第一名了, 因为3号加速度比2号大,2号的存在简直像是可以被消除掉了(

若t1>t2,2号还是能当第一名的,但如果后面又来了个4号,在2号当第一之前超过了2号,2号的存在又可以被消除了,而且4号把2号消除之前,已经把3号消除了

于是可以想到一个单调栈做法,栈内保存的是每个人当第一的时刻,且单调递增, 每次加入一个加速度比栈内所有人都要大的新人,计算新人超越栈顶的时间,如果新人超越栈顶的时刻比栈顶人当第一的时刻小,就把栈顶人pop,栈顶人的存在就被消除了,如果新人超越栈顶的时刻比栈顶人当第一的时刻大,那么目前栈顶人还是能当第一,新人当第一的时刻就是新人超越栈顶的时刻,计录新人的这个时刻并把新人push入栈。如果新人在一开始的时候就是第一,那么他当第一的时刻就是0,栈内元素个数就是当过答案。

至于多人同时保持当第一的情况,就特别处理。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
using namespace std;
const int MAXN = 5e4+7;
const long long INF = 1e18+9;
double eps = 1e-12;
struct RBT{//机器人 
	long long p,a;
	double t = 0.0 ;
}rbt[MAXN],st[MAXN];
bool cmp(RBT x,RBT y){
	if(x.a!=y.a) return y.a>x.a;
	else return y.p>x.p;
}
int main()
{	
	int t,n,h;
	cin>>t;
	while(t--){
		cin>>n;
		int l = 0,r =0;
		for(int i = 1;i<=n;i++){
			scanf("%d%d",&rbt[i].p,&rbt[i].a);
			rbt[i].t = 0.0; //初始化,比赛的时候就是少了这个,wa了几发 
		}
		sort(rbt+1,rbt+n+1,cmp);
		for(int i = 1;i<=n;i++){
			if(r&&rbt[i].p==st[r].p&&rbt[i].a==st[r].a){//特别处理 
				rbt[i].t = st[r].t;
			}
			else while(r>l){
				if(rbt[i].p>=st[r].p){ 
					r--;
					continue;
				}	
				long long da = rbt[i].a-st[r].a;
				long long dx = st[r].p-rbt[i].p;
				long long dvxdv = 2 * da * dx;
				double dv = sqrt((double)dvxdv);
				rbt[i].t = dv/da;
				if(rbt[i].t<=st[r].t+eps) {
					r--;
				}
				else break;
			}
			r++;
			st[r] = rbt[i];
		}
		int ans = r;
		for(int i = 2;i <= r;i++){//把相同第一的同位同速人删掉 
			if(st[i-1].a==st[i].a&&st[i-1].p==st[i].p){
				while(st[i].a==st[i-1].a&&st[i].p==st[i-1].p){
					ans--;
					i++;
				}
				ans--;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
} 

  

 1006 Finding a MEX

比赛时没做出来

把度数大于350的节点称为大节点,其他节点称为小节点,每个大节点建一个对应线段树。

求小节点的MEX就暴力求

求大节点的MEX就要用到线段树,线段树维护的是最小值,查询MEX的时候,如果左半线段的最小值为0,就往左边查询,否则往右半段查询。

每次修改值时,把该节点连接的所有大节点的线段树进行修改,要预处理每个节点连接哪些大节点

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<time.h>
using namespace std;
struct NODE{
	int l, r, mi;
}tree[360][24000];
int val[360][6000];//第i个线段树上某个值出现了几次 
int a[100007];//节点上的值 
int d[100007];//节点的度数 
int ant[100007];//节点对应第几个线段树 
vector<int>graph[100007];//存图 
vector<int> gg[100007];//gg[i] 
bool vis[360];//小节点上的vis 
int tot;
void build(int cnt,int pos,int l,int r){
	tree[cnt][pos].l = l;
	tree[cnt][pos].r = r;
	if(l==r){
		tree[cnt][pos].mi = min(val[cnt][tot],1);
		tot++;
		return;
	}
	int mid = l+r>>1;
	build(cnt,pos<<1,l,mid);
	build(cnt,pos<<1|1,mid+1,r);
	tree[cnt][pos].mi = min(tree[cnt][pos<<1].mi,tree[cnt][pos<<1|1].mi);
}
void modify(int cnt,int pos,int p,int v){
	if(tree[cnt][pos].l==tree[cnt][pos].r){
		tree[cnt][pos].mi = v;
		return;
	}
	int mid = tree[cnt][pos].l+tree[cnt][pos].r>>1;
	if(p<=mid) modify(cnt,pos<<1,p,v);
	else modify(cnt,pos<<1|1,p,v);
	tree[cnt][pos].mi = min(tree[cnt][pos<<1].mi,tree[cnt][pos<<1|1].mi);
}
int query(int cnt,int pos,int l,int r){
	if(tree[cnt][pos].l==tree[cnt][pos].r){
		return tree[cnt][pos].l;
	}
	int mid = tree[cnt][pos].l+tree[cnt][pos].r>>1;
	if(tree[cnt][pos<<1].mi==0) return query(cnt,pos<<1,l,mid);
	else return query(cnt,pos<<1|1,mid+1,r);
}
int main()
{
	int t, n, m, q, u, v;
	cin>>t;
	while(t--){
		cin>>n>>m;
		for(int i = 1;i<=n;i++){
			scanf("%d",&a[i]);
			d[i] = 0;
			graph[i].clear();
			gg[i].clear();
			ant[i] = 0;
		}
		for(int i = 1;i<360;i++){
			for(int j = 0;j<6000;j++){
				val[i][j] = 0;
			}
		}
		for(int i = 1;i <= m;i++){
			scanf("%d%d",&u,&v);
			graph[u].push_back(v);
			graph[v].push_back(u);
			d[u]++;
			d[v]++;
		}
		for(int i = 1;i<=n;i++){//存每个点连接的大节点 
			for(int j = 0;j<graph[i].size();j++){
				int po = graph[i][j];
				if(d[po]>350) gg[i].push_back(po);
			}
		}
		int cnt = 0;
		for(int i = 1;i <= n;i++){//找出度数>350的节点建树 
			if(d[i]>350){
				cnt++;
				ant[i] = cnt;
				tot = 0;
				for(int j = 0;j < d[i];j++){
					int po = graph[i][j];
					if(a[po]>d[i]){
						val[cnt][d[i]]++;
					}
					else val[cnt][a[po]]++;
				}
				build(cnt,1,0,d[i]);
			}
		}
		cin>>q;
		int op,x;
		for(int i = 1;i <= q;i++){
			scanf("%d",&op);
			if(op==1){
				scanf("%d%d",&u,&x);
				//printf("        %d\n",gg[u].size());
				for(int j = 0;j<gg[u].size();j++){ 
					int po = gg[u][j];				
					int lo = a[u];
					if(lo>d[po]) lo = d[po];
					val[ant[po]][lo]--;
					if(!val[ant[po]][lo]){
						modify(ant[po],1,lo,0);
					}
					lo = x;
					if(lo>d[po]) lo = d[po];
					if(!val[ant[po]][lo]){
						modify(ant[po],1,lo,1);
					}
					val[ant[po]][lo]++;					
				}
				a[u] = x;
			}
			else{
				scanf("%d",&u);
				if(d[u]>350){
					printf("%d\n",query(ant[u],1,0,d[u]));
				}
				else{
					for(int j = 0;j<=d[u];j++) vis[j] = false;
					for(int j = 0;j<graph[u].size();j++){
						int po = graph[u][j];
						int lo = a[po];
						if(lo>d[u]) lo = d[u];
						vis[lo] = true;
					} 
					for(int j = 0;j<=d[u];j++){
						if(!vis[j]){
							printf("%d\n",j);
							break;
						}
					}
				}
			}
		}
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/ruanbaitql/p/13429461.html