2019 China Collegiate Programming Contest Qinhuangdao Onsite VP+补题

题目链接:2019 China Collegiate Programming Contest Qinhuangdao Onsite

目录

A.Angle Beats

 E.Escape

F. Forest Program

K. MUV LUV UNLIMITED


A.Angle Beats

题意:给出n (2<=n<=2000) 个点和q (1<=n<=2000) 次查询,询问每次查询的点可以和n个点组成几个直角

思路:可以分出两种情况:

一种情况如果直角的顶点为查询点,那么两条直角边分别为查询点与其中两个固定点的连线,那么就可以对于每一个固定点与查询点连接存斜率,同时在结果加上已经存的可以和这条边连接的边的个数;

另一种情况为如果直角的顶点为固定点,那么我们可以对于每个点分别按照上一种情况的写法把其余固定点作为固定点,该固定点作为查询点,存每个其他固定点依次与该固定点连线的斜率,同时在结果加上与 该固定点与查询点连线垂直的边的个数;对于每次查询分别加上所有固定点作为查询点的情况;

存斜率可以使用pair存分子分母分别除以gcd值,针对x或y为0的情况,可以把斜率存为{1,0},{0,1}  。

代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<math.h>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<list>
//#include<unordered_map>
#define ll long long
#define inf 0x3f3f3f3f3f3f3f3f
#define PII pair<int,int>
#define int  long long
#define endl '\n'
using namespace std;
const int N = 2e3 + 5;
struct stu{
	int x,y;
}a[N],b[N];
int ans[N];
int qq=-1;
map<PII,int>mp;
void ff(int xi,int yi,int i)
{
	int flog;		
	if((xi>0&&yi<0)||(xi<0&&yi>0))
		flog=-1;
	else
		flog=1;
	if(xi!=0&&yi!=0)
	{
		int p=__gcd(abs(xi),abs(yi));
		mp[{abs(xi/p)*flog,abs(yi/p)}]++;
		if(flog==-1)
			ans[i]+=mp[{abs(yi/p),abs(xi/p)}];
		else
			ans[i]+=mp[{abs(yi/p)*qq,abs(xi/p)}];
	}
	else
	{
		if(xi==0)
			mp[{0,1}]++,ans[i]+=mp[{1,0}];
		else
			mp[{1,0}]++,ans[i]+=mp[{0,1}];
	}
}
void f(int xi,int yi)
{
	int flog;		
	if((xi>0&&yi<0)||(xi<0&&yi>0))
		flog=-1;
	else
		flog=1;
	if(xi!=0&&yi!=0)
	{
		int p=__gcd(abs(xi),abs(yi));
		mp[{abs(xi/p)*flog,abs(yi/p)}]++;
	}
	else
	{
		if(xi==0)
			mp[{0,1}]++;
		else
			mp[{1,0}]++;
	}
}
void fi(int xi,int yi,int i)
{
	int flog;		
	if((xi>0&&yi<0)||(xi<0&&yi>0))
		flog=-1;
	else
		flog=1;
	if(xi!=0&&yi!=0)
	{
		int p=__gcd(abs(xi),abs(yi));
		if(flog==-1)
			ans[i]+=mp[{abs(yi/p),abs(xi/p)}];
		else
			ans[i]+=mp[{abs(yi/p)*qq,abs(xi/p)}];
	}
	else
	{
		if(xi==0)
			ans[i]+=mp[{1,0}];
		else
			ans[i]+=mp[{0,1}];
	}
}
void solve() {
	int n,q;
	cin>>n>>q;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i].x>>a[i].y;
	}
	for(int i=1;i<=q;i++)
	{
		cin>>b[i].x>>b[i].y;
		mp.clear();
		for(int j=1;j<=n;j++)
		{
			ff(b[i].x-a[j].x,b[i].y-a[j].y,i);
		}
	}
	for(int i=1;i<=n;i++)
	{
		mp.clear(); 
		for(int j=1;j<=n;j++)
		{
			if(i==j)
				continue;
			f(a[i].x-a[j].x,a[i].y-a[j].y);
		}
		for(int j=1;j<=q;j++)
		{
			fi(a[i].x-b[j].x,a[i].y-b[j].y,j);
		}
	}
	for(int i=1;i<=q;i++)
		cout<<ans[i]<<endl;
	return ;
}
signed main() {
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);
		solve();
	return 0;
}

 E.Escape

题意:给出一个n*m的迷宫,每个方格可以走或者不可以走,在迷宫的上方有a 个机器人,迷宫下方有b个出口,然而,机器人只能沿着它们的移动方向直线前进,不能转弯,初始方向向下,你可以通过放置一些转弯装置帮助机器人转弯,转弯装置只能接受两个方向来的机器人以及在这两个方向上相互转弯,

NE -devices:让上面来的机器人向右走,右边来的机器人向上走.  从左边或下面来是违法的。
NW-devices:让上面来的机器人向左走,左边来的机器人向上走.  从右边或下面来是非法的。
SE -devices:让下面来的机器人向右走,右边来的机器人向下走.  从左边或上面来是非法的。
SW-devices:让下面来的机器人向左走,左边来的机器人向下走.  从右边或上面来是非法的。

每个格子的水平方向和竖直方向都只能被使用一次,因为两 个机器人的路径不可能合并,也不可能迎面相撞。能不能存在一些解决方案可以让所有a个机器人都可以到b个出口中的一个?

思路:这个题一道明显的分层图问题,将图分成垂直方向的图和平行方向的图,起点和终点分别在垂直图连接源点和汇点,每个点垂直点和平行点相互连接,每个垂直点与他上下的垂直点连接,每个平行点与他左右的平行点连接,通量均为1,跑最大流即可,最大流小于a,则输出“No”,否则输出“Yes”。

代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<math.h>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<list>
//#include<unordered_map>
#define endl '\n'
#define PI acos(-1.0)
#define int long long
#define PII pair<pair<int,int>,int>
//priority_queue<PII,vector<PII>,greater<PII> >q;
using namespace std;
const int N=2e4+50;
const int M=N*6;
const int inf=0x3f3f3f3f3f3f3f3f;
string gg[105];
int n,m;
int s=N-11,t=N-10;
map<PII,int>mp;
int cnt=0;
int lists[N],cur[N]; //记录路径,当前弧优化标记增广起点 
int gnext[M],gto[M],gdist[M];
int first[N];
int id(int x,int y,int q)
{
	if(!q)
		return x*m+y+1;
	else
		return x*m+y+1+n*m;
}
void add(int u,int v,int w)
{
	gnext[cnt]=first[u];
	gto[cnt]=v;
	gdist[cnt]=w;
	first[u]=cnt++;
}
bool BFS()
{
	memset(lists,-1,sizeof(lists)); //路径初始化 
	queue<int>Q;
	lists[s]=1;
	cur[s]=first[s];	//当前弧优化初始化 
	Q.push(s);
	while(Q.size())
	{
		int f=Q.front();
		Q.pop();
		for(int i=first[f];i!=-1;i=gnext[i])
		{
			int to=gto[i],dist=gdist[i];
			if(dist>0&&lists[to]==-1)
			{
				cur[to]=first[to],lists[to]=lists[f]+1;
				if(to==t)	return 1;
				Q.push(to);
			}
		}
	}
	return 0;
}
int DFS(int p=s,int flow=inf)
{
	if(p==t)
		return flow;
	int rmn=flow;
	for(int i=cur[p];i!=-1&&rmn;i=gnext[i])
	{
		cur[p]=i;
		int to=gto[i],dist=gdist[i];
		if(dist>0&&lists[to]==lists[p]+1)
		{
			int c=DFS(to,min(rmn,dist));
			rmn-=c;
			gdist[i]-=c;
			gdist[i^1]+=c;
		}
	}
	return flow-rmn;
}
int dinic()
{
	int ans=0;
	while(BFS())
	{
		ans+=DFS();
	}
	return ans;
}
void solve()
{
	int a,b;
	cnt=0;
	cin>>n>>m>>a>>b;
	for(int i=1;i<=2*n*m;i++)
		first[i]=-1;
	first[s]=-1,first[t]=-1;
	for(int i=0;i<n;i++)
		cin>>gg[i];
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)
		{
			if(gg[i][j]=='0')
			{
				int f=id(i,j,0);
				if(i!=0&&gg[i-1][j]=='0')
				{
					int x=id(i-1,j,0);
					add(f,x,1);
					add(x,f,1);
				}
				int q=f;
				f=id(i,j,1);
				if(j!=0&&gg[i][j-1]=='0')
				{
					int x=id(i,j-1,1);
					add(f,x,1);
					add(x,f,1);					
				}
				add(f,q,1);
				add(q,f,1);
			}
		}
	}
	int x;
	for(int i=1;i<=a;i++)
	{
		cin>>x;
		if(gg[0][x-1]=='0')
		{
			int q=id(0,x-1,0);
			add(s,q,1);
			add(q,s,0);			
		}
	}	
	for(int i=1;i<=b;i++)
	{
		cin>>x;
		if(gg[n-1][x-1]=='0')
		{
			int q=id(n-1,x-1,0);
			add(q,t,1);
			add(t,q,0);		
		}
	}
//	cout<<dinic()<<endl;
	if(dinic()<a)
		cout<<"No"<<endl;
	else
		cout<<"Yes"<<endl;
}
signed main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);
	int T;
	cin>>T;
	while(T--)
		solve();
	return 0;
}

F. Forest Program

题意:给出一个连接的无向图,删除一些边,让这个无向图形成树,也就是说剩下的图中没有环,有多少种删边方法,结果对998244353取模

思路:很明显对于每个环有n条边来说,可以删去1~n的容易数量的环,剩下不形成环的m条边,可以选择删去0~m条边,总情况就应该为每个环的情况(2^n-1)与链的情况(2^m)相乘取模;

代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<math.h>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<list>
//#include<unordered_map>
#define endl '\n'
#define PI acos(-1.0)
#define int long long
#define PII pair<int,int>
//priority_queue<PII,vector<PII>,greater<PII> >q;
using namespace std;
const int N = 3e5 + 5;
const int maxn = 5e5 + 5;
const int mod=998244353;
int n,m,u,v;
int sum=1;
vector<int>g[N];
vector<int>ans;
set<int>mp;
map<PII,int>mp1;
int f[N];
int ff[maxn];
void dfs(int x)
{
	for(int i=0;i<g[x].size();i++)
	{
		if(!f[g[x][i]])
		{
			f[g[x][i]]=f[x]+1;
			mp.erase(g[x][i]);
			dfs(g[x][i]);
		}			
		else
		{
			if(abs(f[g[x][i]]-f[x])!=1&&!mp1[{g[x][i],x}])
			{
				ans.push_back(abs(f[g[x][i]]-f[x])+1);
				m-=abs(f[g[x][i]]-f[x])+1;
				mp1[{g[x][i],x}]=1;
				mp1[{x,g[x][i]}]=1;
			}
		}
	}
}
void solve() {
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		mp.insert(i);
	for(int i=1;i<=m;i++)
	{
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	if(!m){
		cout<<1<<endl;
		return;
	}
	while(!mp.empty())
	{
		auto t=mp.begin();
		int x=(*t);
		f[x]=1;
		mp.erase(x);
		dfs(x);
	}
	for(int i=0;i<ans.size();i++)sum=(sum*(ff[ans[i]]-1))%mod;
	cout<<(sum*ff[m])%mod<<endl;
}
signed main() {
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);
	ff[0]=1;
	for(int i=1;i<maxn;i++){
		ff[i]=ff[i-1]*2%mod;
	}
//	int _;
//	cin >> _;
//	while (_--)
		solve();
	return 0;
}

K. MUV LUV UNLIMITED

题意:两个人做游戏,给出一个树,每个人每次可以选择删除1个或者多个当前的叶子节点,如果每次操作都为最优,不能操作的人输掉,问你先手必胜还是后手必胜,先手打印"Takeru",后手打印"Meiya"。

思路:分为两个情况:

只有一条链的情况,直接判断这条链的奇偶即可;

另一种情况,使用vector存储当前节点的子节点有多少个链以及链的长度,如果该节点没有子节点即为叶子节点,则在vector加一个长度为1的链传递给他的父亲,对于每个父亲节点接收到子节点传到的数量如果只有一条链,则给这条链的长度加1,否则不加,然后传递给他的父亲节点。最后判断1的子节点的链长有没有为奇数,有则先手必胜,否则后手必胜

代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<math.h>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<list>
//#include<unordered_map>
#define endl '\n'
#define PI acos(-1.0)
#define int long long
#define PII pair<int,int>
//priority_queue<PII,vector<PII>,greater<PII> >q;
using namespace std;
const int N=1e6+5;
bool cmp(int a,int b)
{
	return a>b;
}
vector<int>g[N];
vector<int> dfs(int p,int x)
{
	vector<int>a,b; 
	for(int i=0;i<g[x].size();i++)
	{
		if(g[x][i]!=p)
		{
			b=dfs(x,g[x][i]);
			for(int i=0;i<b.size();i++)
			{
				a.push_back(b[i]);
			}			
		}
	}
	if(a.empty())
		a.push_back((int)1);
	else if(a.size()==1)
		a[0]++;			
	return a;
}
void solve()
{
	int n,x;
	cin>>n;
	for(int i=1;i<=n;i++)
		g[i].clear();
	for(int i=2;i<=n;i++)
	{
		cin>>x;
		g[i].push_back(x);
		g[x].push_back(i);
	}
	vector<int>a;	
	a=dfs(-1,1);
	int flog=0;
	for(int i=0;i<a.size();i++)
	{
		if(a[i]%2==1)
		{
			flog=1;
			break;
		}			
	}
	if(flog)
		cout<<"Takeru"<<endl;
	else
		cout<<"Meiya"<<endl;
}
signed main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);
	int T;
	cin>>T;
	while(T--)
		solve();
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值