Codeforces Round #809 (Div. 2)
作者:互联网
加粗:赛时AC
普通:赛后AC
A. Another String Minimization Problem
水题,先变顺序在前面的再变后面的
B. Making Towers
我们考虑对于每个位置i,它的状态能从哪里转移过来。
比较显然,当i-1、i-3、i-5这些位置的值和i一样的时候,可以发生转移。我们来证明这些位置里离i最近的一定不劣与远于该位置的状态。
设dp[i]为该位置的最优值,对于i-2、i-4这些位置,如果存在上一个转移过来的状态位置设为j,那么j能够转移到i-2、i-4这些位置的同时,一定也能转移到i,因此dp函数对于同种颜色具有单调性,所以每个状态只需要关注和自己颜色一样的能转移过来的最近的位置。
int main() { read(T); while(T--) { read(m); for(int i=1;i<=m;i++) { read(a[i]); dp[a[i]][i%2]=dp[a[i]][(i-1)%2]+1; ans[a[i]]=max(dp[a[i]][i%2],ans[a[i]]); } for(int i=1;i<=m;i++) { cout<<ans[i]<<" "; } cout<<endl; memset(dp,0,sizeof(dp)); memset(ans,0,sizeof(ans)); } return 0; }View Code
C. Qpwoeirut And The City
我们首先要满足最多,其次要满足楼梯数最少。
然后有一个性质,我们不可能同时变换相邻的两个建筑,因此答案每幢楼的开销是一个固定值。
假设楼的幢数是奇数,那么显然将偶数答案全部加起来就可以。
对于偶数,首先明确答案数是(n-2)/2,对于全选偶数的答案,我们在某个位置开始后面的值偏移一位就是一个新的选择方案,这个过程可以用前缀和进行模拟。
int main() { read(T); while(T--) { read(n); for(int i=1;i<=n;i++) { read(h[i]); sum[i]=0; } for(int i=2;i<n;i++) { p[i]=max(h[i-1],h[i+1])-h[i]+1; p[i]=max(1ll*0,p[i]); sum[i]=sum[i-2]+p[i]; } ll ans=1000000000000000000; for(int i=2;i<n;i++) { if(i%2==0) ans=min(sum[i]+(sum[n-1]-sum[i+1]),ans); } if(n%2==1) printf("%lld\n",sum[n-1]); else { printf("%lld\n",min(ans,sum[n-1])); } } return 0; }View Code
D1. Chopping Carrots (Easy Version)
(我好像看到了二分的做法?)
由于数据按顺序给出,我们可以知道最小值一定是小于等于a[1]的某个值。
我们直接枚举最小值,然后去计算所有后面的值在不小于我们确定的最小值的情况下能除得的最小值,然后将将这之中的最大值减掉枚举的最小值得到当前答案,对所有答案取min。
int main() { read(T); while(T--) { read(n);read(k); for(int i=1;i<=n;i++) read(a[i]); int ans=10000000; for(int i=0;i<=a[1];i++) { int lim=0; for(int j=1;j<=n;j++) { int p; if(i==0) p=k; else p=min(k,a[j]/i); lim=max(a[j]/p,lim); } ans=min(ans,lim-i); } printf("%d\n",ans); } return 0; }View Code
D2. Chopping Carrots (Hard Version)
延续上一题的思路,现在数据范围增大了,枚举最小值然后计算答案的n方复杂度不再能够被接受,因此要转换思路。
我们可以发现,某个数字x去除以小于它自己的数字y(0<y<=x)的时候,实际上会出现许多重复的答案,这个答案的边界在(x/(x/y))。
所以我们将重点放在这些答案上,去计算每个数字相除得到的这些答案,然后直接跳到下个边界(这个过程复杂度接近O(sqrt(a[i]))),然后用一个数组p记录,p[i]仍旧表示在最小值为i的情况下各个数字能得到的最接近的值的最大值。
更新的时候,每次我们得到一个当前计算后的值k,然后用上一次的答案k'更新p[k+1],意味着现在答案可以取到k了,则最小值为k+1的时候大于等于它的是上一次的答案取最大值。
int main() { read(T); while(T--) { read(n);read(k); for(int i=1;i<=n;i++) read(a[i]); memset(ps,0,sizeof(ps)); for(int i=1;i<=n;i++) { int lim=1000000000; for(int j=1;j<=min(k,a[i]);j=(a[i]/(a[i]/j))+1) { int kk=a[i]/j; ps[kk+1]=max(ps[kk+1],lim); lim=kk; } ps[0]=lim; } int ans=1000000000; int limm=0; for(int i=0;i<=a[1];i++) { limm=max(limm,ps[i]); ans=min(ans,limm-i); } printf("%lld\n",ans); } return 0; }View Code
E. Qpwoeirut and Vertices
这题就是RMQ最值+重构树的板子题,只要知道一个性质:
一堆点的LCA实际上就是这些点dfs序最小的点和最大的点的LCA,根据dfs序获得的顺序可以证明(一个点的子树一定在它的后面出现,相互之间要么全包含,要么没有交点)
上面性质用RMQ维护,然后按给定的边的顺序去做重构树,直接求一个虚点。
我直接复制拼接了树剖LCA+ST表+重构树的板子,所以有点乱。
#include<iostream> #include<cstdio> #include<algorithm> #include<queue> #include<cstring> #include<cmath> #define ll long long #define N 601000 using namespace std; int n,m,k,tot,Q,tt,T,cnt; int f[N],key[N*4]; int top[N],dep[N],faz[N],son[N],siz[N],tid[N],en[N]; int rnk[N]; int st1[30][N],st2[30][N]; struct node { int v,u,w; friend bool operator > (node a,node b){return a.w>b.w;} }; priority_queue<node,vector<node>,greater<node> >que; struct road {int fro,too,next; }ed[N*4]; int heads[N]; inline void read(int &p) { p=0; int f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9') {p=p*10+(ch-'0');ch=getchar();} p*=f; } inline int find(int x) { if(f[x]==x) return x; return f[x]=find(f[x]); } inline void addin(int a,int b) { ed[++tot].next=heads[a]; ed[tot].fro=a; ed[tot].too=b; heads[a]=tot; ed[++tot].next=heads[b]; ed[tot].fro=b; ed[tot].too=a; heads[b]=tot; } inline void dfs1(int x) { siz[x]=1; int maxx=-1; for(int i=heads[x];i;i=ed[i].next) { int v=ed[i].too; if(!dep[v]) { dep[v]=dep[x]+1; faz[v]=x; dfs1(v); siz[x]+=siz[v]; if(siz[v]>maxx) { maxx=siz[v]; son[x]=v; } } } } inline void dfs2(int x,int t) { tid[x]=++cnt; top[x]=t;rnk[cnt]=x; if(!son[x]) return ; dfs2(son[x],t); for(int i=heads[x];i;i=ed[i].next) { int v=ed[i].too; if(dep[v]>dep[x]&&son[x]!=v) dfs2(v,v); } } inline int LCA(int x,int y) { while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); x=faz[top[x]]; } if(dep[x]<dep[y]) return x; return y; } inline void bfs() { while(!que.empty()) { node lim=que.top(); que.pop(); int u=lim.u; int v=lim.v; int fa=find(u); int fb=find(v); if(fa==fb) continue; f[fa]=f[fb]=++tt; //ע��˴����鼯�÷� addin(tt,fa); addin(tt,fb); key[tt]=lim.w; } } inline void ycl() { for (int j = 0;j <= 20;++j) { for (int i = 1;i + (1 << j) - 1 <= n;++i) { if (!j) st1[j][i] = st2[j][i] = tid[i]; else { st1[j][i] = min(st1[j-1][i],st1[j-1][i + (1 <<j-1)]); st2[j][i] = max(st2[j-1][i],st2[j-1][i + (1 <<j-1)]); } } } } int query1(int l,int r) { int now = log(r - l + 1) / log(2); return min(st1[now][l],st1[now][r - (1 << now) + 1]); } int query2(int l,int r) { int now = log(r - l + 1) / log(2); return max(st2[now][l],st2[now][r - (1 << now) + 1]); } inline void init() { cnt=0; tot=0; for(int i=0;i<=3*n;i++) { heads[i]=0; dep[i]=0; son[i]=0; tid[i]=0; key[i]=0; siz[i]=0; } while(!que.empty()) que.pop(); } int main() { read(T); while(T--) { read(n);read(m);read(Q); init(); for(int i=1;i<=n*3;i++) f[i]=i; for(int i=1;i<=m;i++) { node lim; read(lim.u);read(lim.v); lim.w=i; que.push(lim); } tt=n; bfs(); dep[tt]=1; dfs1(tt); dfs2(tt,tt); ycl(); int x,y; while(Q--) { int l,r; read(x);read(y); l=rnk[query1(x,y)]; r=rnk[query2(x,y)]; printf("%d ",key[LCA(l,r)]); } printf("\n"); } return 0; }View Code
https://www.luogu.com.cn/problem/P2245 重构树题目
标签:while,int,最小值,Codeforces,read,809,答案,Div,include 来源: https://www.cnblogs.com/ztlsw/p/16505238.html