题意:平面上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]);
else
even.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;
}