第一场是朝鲜出题
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; }