CS R23 C(排列循环节+前缀和),D(好题,列式子,取交集), E(套路,二分图,最大独立集,最小割)

来源:互联网 发布:js设置颜色渐变 编辑:程序博客网 时间:2024/05/21 10:55
Problem B:
题意:平面上n个点,任意两点距离为其曼哈顿距离,n个点中有些点是特殊的.特殊点间的有快速通道为距离T.
Q次询问:A->B的最短距离.Q,n<=1e3.
建图? 不用阿,两点之间直线最短,所以A->B要么为dis(a,b),

要么走快速通道 暴力枚举一个离a最近的特殊点即可 O(n^2).

#include <bits/stdc++.h>using namespace std;typedef long long ll;typedef pair<int,int> ii;const int N=2e5+20,inf=0x3f3f3f3f;int n,T,q;struct node{int s,x,y;}a[N];int main(){cin>>n>>T;for(int i=1;i<=n;i++)scanf("%d%d%d",&a[i].s,&a[i].x,&a[i].y);cin>>q;while(q--){int s,t;scanf("%d%d",&s,&t);int dis=abs(a[s].x-a[t].x)+abs(a[s].y-a[t].y);int d1=inf,d2=inf;for(int i=1;i<=n;i++){if(a[i].s==0)continue;int t1=abs(a[s].x-a[i].x)+abs(a[s].y-a[i].y);int t2=abs(a[t].x-a[i].x)+abs(a[t].y-a[i].y);d1=min(d1,t1);d2=min(d2,t2);}int ans=min(dis,d1+d2+T);cout<<ans<<endl;}return 0;}
Problem C:
题意:给出长度为m的排列a. 现在排列a会生成一个矩阵b
矩阵b:
第一行为 a[1],a[2]..a[j]..a[m].
第二行为 a[a[1]],a[a[2]]..a[a[j]]..a[a[m]].
...
第i行元素为 ..a[a[a[a[..a[j]]..总共套有i个a
n,m<=1e5. 求矩阵中每列元素之和.


看第j列 a[j],a[a[j]]... 
找到a[j]所在循环节的长度,记录每个循环节的和,每个元素在自己循环节中的位置(用前缀和算n%size部分),O(N+M)

#include <bits/stdc++.h>using namespace std;typedef long long ll;typedef pair<int,int> ii;const int N=2e5+20;int n,m,a[N],vis[N],bel[N],sum[N],pos[N];vector<int> v[N],pre[N];int main(){int cnt=0;scanf("%d%d",&m,&n);for(int i=1;i<=m;i++)scanf("%d",&a[i]);for(int i=1;i<=m;i++){if(vis[i])continue;int cur=i;++cnt;while(!vis[cur]){v[cnt].push_back(cur);bel[cur]=cnt,sum[cnt]+=a[cur],pos[cur]=v[cnt].size();vis[cur]=1;cur=a[cur];}}for(int i=1;i<=cnt;i++){pre[i]=v[i];for(int j=0;j<v[i].size();j++)pre[i].push_back(v[i][j]);for(int j=1;j<pre[i].size();j++)pre[i][j]=pre[i][j-1]+pre[i][j];}//cout<<cnt<<endl;for(int i=1;i<=m;i++){ll num=bel[i];ll time=n/v[num].size();ll re=n%v[num].size();ll res=pre[num][pos[i]+re-1]-pre[num][pos[i]-1];//a[i]ºóÃære¸öÖ®ºÍ? ¸´ÖÆÒ»±é ÀûÓÃǰ׺ºÍÀ´Çó. printf("%lld ",time*sum[num]+res);}printf("\n");return 0;}
Problem D

题意:数轴上n个圆心x[i],第i个圆心的可选半径为[a[i],b[i]].
n<=1e5,a[i],b[i],x[i]<=1e9.问相邻圆都相切时 选择半径的方案数?

第一个点的半径确认了 其余的也就固定了 可是第一个点半径范围r[1]最坏在1e9左右.
r[1]有单调性,?? 若y=r[1]不可行 则y要么是太大 要么是太小了. 二分m时 无法判定m过大还是过小.

d[i]设为(x[i+1],x[i])之间距离
当第一个半径r[1]为x时
r[2]=d1-x;
r[3]=d2-r[2]=d2-d1+x
r[4]=d3-r[3]=d3-d2+d1+x.
..
r[i]=d[i]-d[i-1]+d[i-2]-....((-1)^(i+1)%2 )*x

a[2]<=r[2]<=b[2]
d[1]-b[2]<=x<=d[1]-a[2]
..
因为r[i]是在某个范围(a[i],b[i])内的,d[i]又是常数.
上面每个等式都可以确定x某一段范围,则可行的方案必须在这些区间的交集上,方法数为交集的大小

#include <bits/stdc++.h>using namespace std;typedef long long ll;const int N=2e5+20,inf=0x3f3f3f3f;ll n,x[N],a[N],b[N];int main(){cin>>n;for(int i=1;i<=n;i++)scanf("%lld",&x[i]);for(int i=1;i<=n;i++)scanf("%lld%lld",&a[i],&b[i]);ll l=a[1],r=b[1];ll sum=0;for(int i=2;i<=n;i++){sum*=-1;sum+=(x[i]-x[i-1]);if(i%2){l=max(l,a[i]-sum);r=min(r,b[i]-sum);}else{l=max(l,sum-b[i]);r=min(r,sum-a[i]);}}cout<<max(0ll,r-l+1)<<endl;return 0;}


Problem E:
题意:给出n个不同的数,操作:删除任意一个数.
当n个数中任意两个相加都不为prime时,最小需要的操作次数?,并输出删除的点.n<=2000,a[i]<=1e5.

当a[i]+a[j]为prime时 a[i]-a[j]一条边,删除数最小也就是剩下元素最多,求出该图的最大独立集就好了.
奇数和偶数分成两部分,只有奇数到偶数才有可能有边,该图还是个二分图.

最大独立集=总的点数-最小点覆盖.
最小点覆盖:令(u,v)容量为inf,则求最小割时(s,u),(v,t)至少一条在最小割上,满足最小点覆盖要求.
求出最小割后 从s开始dfs一遍 标记到达的点 就能知道那些边是满流边了;(不能到p1则s-p1为满流,能到p2说明p2->t必须为满流)
满流的边不一定是割边!!!!

#include <bits/stdc++.h>using namespace std;typedef long long ll;const int N=2e5+20,inf=0x3f3f3f3f,MAXN=5e5;int vis[N],s,t,p1,p2,ans,cnt;int n,a[N];vector<int> odd,even;void table(){for(int i=2;i<N;i++){if(!vis[i])for(int j=i+i;j<N;j+=i)vis[j]=1;}}struct edge{      int from,to,cap,flow;  };  struct Dinic{      int n,m,s,t;      int vis[MAXN];      int d[MAXN];      int cur[MAXN];      vector<int> G[MAXN];      vector<edge> edges;      void init(int n){          this->n=n;          for(int i=0;i<n;++i)G[i].clear();          edges.clear();      }      void adde(int from,int to,int cap){          edges.push_back(edge{from,to,cap,0});          edges.push_back(edge{to,from,0,0});          int m=edges.size();          G[from].push_back(m-2);          G[to].push_back(m-1);      }      bool BFS(){          memset(vis,0,sizeof(vis));          vis[s]=1;          d[s]=0;          queue<int> q;          q.push(s);          while(!q.empty()){              int x=q.front();              q.pop();              for(int i=0;i<G[x].size();++i){                  edge& e=edges[G[x][i]];                  if(!vis[e.to]&&e.cap>e.flow){                      d[e.to]=d[x]+1;                      vis[e.to]=1;                      q.push(e.to);                  }              }          }          return vis[t];      }        int DFS(int x,int a){          if(x==t||a==0)return a;          int flow=0,f;          for(int &i=cur[x];i<G[x].size();++i){              edge& e=edges[G[x][i]];              if(d[e.to]==d[x]+1&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0){                  e.flow+=f;                  edges[G[x][i]^1].flow-=f;                  flow+=f;                  a-=f;                  if(a==0)break;              }          }          return flow;      }      int Maxflow(int s,int t){          this->s=s,this->t=t;          int flow=0;          while(BFS()){              memset(cur,0,sizeof(cur));              flow+=DFS(s,inf);          }          return flow;      }void dfs(int u){vis[u]=1;for(int i=0;i<G[u].size();i++){edge e=edges[G[u][i]];if(!vis[e.to]&&e.flow<e.cap)dfs(e.to);}}  }g;  int main(){scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);table();for(int i=1;i<=n;i++){if(a[i]%2)odd.push_back(a[i]);elseeven.push_back(a[i]);}p1=odd.size(),p2=even.size();s=p1+p2,t=s+1;g.init(t+1); for(int i=0;i<odd.size();i++)g.adde(s,i,1);for(int i=0;i<even.size();i++)g.adde(i+p1,t,1); for(int i=0;i<odd.size();i++) for(int j=0;j<even.size();j++) if(!vis[odd[i]+even[j]]) g.adde(i,p1+j,inf); printf("%d\n",ans=g.Maxflow(s,t)); memset(g.vis,0,sizeof(g.vis));g.dfs(s);for(int i=0;i<p1;i++)if(!g.vis[i])printf("%d ",odd[i]);for(int i=0;i<p2;i++)if(g.vis[i+p1])printf("%d ",even[i]);printf("\n");return 0;}





阅读全文
0 0