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

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]);
		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;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值