[关闭]
@rebirth1120 2019-11-04T20:54:04.000000Z 字数 4274 阅读 593

2019,11,4 长郡 csp

考试


今天又考炸了...真的有点无助...
前几天的题还没改完, 所以就粗糙地记录一下吧.

T1 学农 farm

题意

平面内有若干个点, 每个点有一个权值, 选定一个位置, 使得所有点到达这个位置的曼哈顿距离加上权值的最大值最小.

思路

"最大值最小", 果断二分,
但是我当时完全没用脑子....感觉不会check, 就跑了....

二分答案, 然后每个点的活动范围是一个菱形, 对这些菱形求交就行了(维护上下界, 左右界)

然后其实只要把这些点按照它们的权值拆成四个点, 然后找到一个最小的菱形, 使它能包围所有点就行了.

代码

  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. #define db double
  4. using namespace std;
  5. const int N=1e6+7;
  6. const ll inf=1e12;
  7. int n;
  8. ll x[N],y[N],t[N];
  9. db ans;
  10. ll gi(){
  11. char c=getchar(); ll x=0,f=1;
  12. while(c<'0'||c>'9'){ f=c=='-'?-1:1; c=getchar(); }
  13. while(c>='0'&&c<='9'){ x=(x<<3)+(x<<1)+c-'0'; c=getchar(); }
  14. return x*f;
  15. }
  16. int main(){
  17. // freopen("farm2.in","r",stdin);
  18. n=(int)gi();
  19. ll b1=-inf,b2=inf,b3=-inf,b4=inf;
  20. for(int i=1;i<=n;i++){
  21. x[i]=gi(); y[i]=gi(); t[i]=gi();
  22. b1=max(b1,y[i]-x[i]+t[i]);
  23. b2=min(b2,y[i]-x[i]-t[i]);
  24. b3=max(b3,y[i]+x[i]+t[i]);
  25. b4=min(b4,y[i]+x[i]-t[i]);
  26. }
  27. ans=max((b1-b2)/2.0,(b3-b4)/2.0);
  28. printf("%lf\n",ans);
  29. return 0;
  30. }

T2 平面嵌入 planar

题意

平面上一堆点, 构成了一个网格, 其中某些格子的主对角线上有边(也就是固定了),
问是否存在一种点的位置分配方案, 使得存在一个格子的主对角线上的两个点的距离 .

解法

一道神奇的题目...考场上想了好久, 什么都没想出来.....

首先, 如果一整列或一整行上面不存在固定了的格子, 那么这一行/列可以集体移动,

设存在一个固定了的格子(i,j), 若要移动第 i 行, 则第 j 列也要移动, 才能使这个格子不变形,

那我们对于每个固定了的格子(i,j), 都在 i,j 间连一条无向边, 若所有的行和列都在同一个联通块中, 则不存在一个合法的方案 (因为如果所有的行和列都移动了, 就相当于整个网格都移动了, 形状没有变化).

否则的话, 就随便找一个连通块, 把要移动的移动了就行了. (还要注意一下精度问题)

代码

  1. #include<bits/stdc++.h>
  2. #define db double
  3. using namespace std;
  4. const int N=1000+7;
  5. const int M=250000+7;
  6. int r,c,m;
  7. db x[M],y[M],q2;
  8. bool fd[M],mp[N][N],vis[N];
  9. int main(){
  10. // freopen("planar.in","r",stdin);
  11. // freopen("planar.out","w",stdout);
  12. cin>>r>>c>>m; int t;
  13. for(int i=1;i<=m;i++){ scanf("%d",&t); fd[t]=1; }
  14. for(int i=1;i<r;i++)
  15. for(int j=1;j<c;j++)
  16. if(fd[(i-1)*c+j]){ mp[i+1][j+1+r]=mp[j+1+r][i+1]=1; }
  17. queue<int> q; int cnt=0;
  18. q.push(2); vis[2]=1;
  19. while(!q.empty()){
  20. int u=q.front(); q.pop(); cnt++;
  21. for(int v=1;v<=r+c;v++)
  22. if(mp[u][v]&&!vis[v]){ q.push(v); vis[v]=1; }
  23. }
  24. if(cnt==r+c-2){ puts("No"); return 0; }
  25. x[1]=y[1]=0; q2=1/sqrt(2);
  26. for(int i=1;i<=r;i++){
  27. int k=(i-1)*c+1;
  28. if(vis[i]){ x[k]=x[k-c]+q2; y[k]=y[k-c]+q2; }
  29. else if(i!=1){ x[k]=x[k-c]; y[k]=y[k-c]+1; }
  30. for(int j=2;j<=c;j++){
  31. int k=(i-1)*c+j;
  32. if(vis[j+r]){ x[k]=x[k-1]+q2; y[k]=y[k-1]-q2; }
  33. else{ x[k]=x[k-1]+1; y[k]=y[k-1]; }
  34. }
  35. }
  36. puts("Yes");
  37. for(int i=1;i<=r;i++)
  38. for(int j=1;j<=c;j++)
  39. printf("%.8lf %.8lf\n",x[(i-1)*c+j],y[(i-1)*c+j]); //这里如果按照默认的输出小数点后 6 位会过不了spj
  40. return 0;
  41. }

T3 小球 ball

题意

有一棵树, 有若干次操作 (op,u),

若 op==1, 则在节点 u 放一个小球, 若 u 的子树中有空位, 小球会掉向 u 的子节点中, 子树中最小值最小的一个子节点, 并重复这个过程.
若op==2, 则删除节点 u 的球.

保证 op==1 时, u 上没有球, op==2 时, u 上有球.

思路

最后四十分钟, 题意都没看懂....................................................
题解的思路用到了kruskal重构树, 没太看懂, 但是做法应该都差不多.

结论 : 树上点的优先级等价于 用大根堆进行拓扑排序 得到的序列 的反序列.

我是这样理解的, 每次我们都要选择一个最小且未被覆盖的点, 然后再在它的子树中重复这个操作,
那么, 节点子树中的点的优先级大于根节点的优先级,
而对于没有附属关系的点, 数值小的点的优先级大于数值大的点优先级,
所以...拓扑排序 + 大根堆...

代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=7e5+7;
  4. const int L=20;
  5. const int inf=0x3f3f3f3f;
  6. int n,q,de[N],cnt,num[N],dfn[N],en[N],rec[N],f[N][L+7],p[4*N];
  7. int lst[N],nxt[N],to[N],tot;
  8. bool b[N];
  9. priority_queue<int> h;
  10. int gi(){
  11. char c=getchar(); int x=0,f=1;
  12. while(c<'0'||c>'9'){ f=c=='-'?-1:1; c=getchar(); }
  13. while(c>='0'&&c<='9'){ x=(x<<3)+(x<<1)+c-'0'; c=getchar(); }
  14. return x*f;
  15. }
  16. void add(int x,int y){ nxt[++tot]=lst[x]; to[tot]=y; lst[x]=tot; }
  17. void read(){
  18. n=gi(); q=gi();
  19. for(int i=2;i<=n;i++){
  20. f[i][0]=gi();
  21. add(f[i][0],i); de[i]++;
  22. }
  23. }
  24. void topo(){
  25. h.push(1); num[0]=-inf;
  26. while(!h.empty()){
  27. int u=h.top(); h.pop();
  28. num[u]=++cnt;
  29. for(int i=lst[u];i;i=nxt[i]){
  30. de[to[i]]--;
  31. if(!de[to[i]]) h.push(to[i]);
  32. }
  33. }
  34. cnt=0;
  35. }
  36. void pre(int u){
  37. dfn[u]=++cnt; rec[cnt]=u;
  38. for(int i=1;i<=L;i++) f[u][i]=f[f[u][i-1]][i-1];
  39. for(int i=lst[u];i;i=nxt[i]) pre(to[i]);
  40. en[u]=cnt;
  41. }
  42. void build(int k,int l,int r){
  43. if(l==r){ p[k]=rec[l]; ; return; }
  44. int mid=(l+r)>>1;
  45. build(k<<1,l,mid);
  46. build(k<<1|1,mid+1,r);
  47. p[k]= num[p[k<<1]]>num[p[k<<1|1]] ?p[k<<1] :p[k<<1|1];
  48. }
  49. void modify(int k,int l,int r,int x,int t){
  50. if(l==r&&l==r){ p[k]=t; return; }
  51. int mid=(l+r)>>1;
  52. if(x<=mid) modify(k<<1,l,mid,x,t);
  53. else modify(k<<1|1,mid+1,r,x,t);
  54. p[k]= num[p[k<<1]]>num[p[k<<1|1]] ?p[k<<1] :p[k<<1|1];
  55. }
  56. int query(int k,int l,int r,int x,int y){
  57. if(l>=x&&r<=y) return p[k];
  58. int mid=(l+r)>>1,t1=0,t2=0;
  59. if(x<=mid) t1=query(k<<1,l,mid,x,y);
  60. if(y>mid) t2=query(k<<1|1,mid+1,r,x,y);
  61. return num[t1]>num[t2] ?t1 :t2;
  62. }
  63. int find(int x){
  64. for(int i=L;i>=0;i--)
  65. if(b[f[x][i]]) x=f[x][i];
  66. return x;
  67. }
  68. void run(){
  69. int op,x;
  70. for(int i=1;i<=q;i++){
  71. op=gi(); x=gi();
  72. if(op==1){
  73. int t=query(1,1,n,dfn[x],en[x]);
  74. printf("%d\n",t); b[t]=1;
  75. modify(1,1,n,dfn[t],0);
  76. }
  77. else{
  78. int t=find(x); b[t]=0;
  79. modify(1,1,n,dfn[t],t);
  80. }
  81. }
  82. }
  83. int main(){
  84. // freopen("ball.in","r",stdin);
  85. // freopen("ball.out","w",stdout);
  86. read();
  87. topo();
  88. pre(1);
  89. build(1,1,n);
  90. run();
  91. return 0;
  92. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注