寒假集训第一期
题目来源:https://vjudge.net/contest/536804
第一周基础知识
A题 Epic Game
Simon and Antisimon play a game. Initially each player receives one fixed positive integer that doesn't change throughout the game. Simon receives number a and Antisimon receives number b. They also have a heap of n stones. The players take turns to make a move and Simon starts. During a move a player should take from the heap the number of stones equal to the greatest common divisor of the fixed number he has received and the number of stones left in the heap. A player loses when he cannot take the required number of stones (i. e. the heap has strictly less stones left than one needs to take).
Your task is to determine by the given a, b and n who wins the game.
- 辗转相除法求gcd
点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
int main(){
    int a,b,n,res,i=1;
    cin>>a>>b>>n;
    while(i){
        if(i%2){
            int res=gcd(a,n);
            //cout<<n<<" "<<res<<" "<<i<<endl;
            if(n>=res)
                n-=res;
            else{
                cout<<1;
                break;
            }
        }
        else{
            int res=gcd(b,n);
            //cout<<n<<" "<<res<<" "<<i<<endl;
            if(n>=res)
                n-=res;
            else{
                cout<<0;
                break;
            }
        }
        i++;
    }
}
B题 Die Roll
Yakko, Wakko and Dot, world-famous animaniacs, decided to rest from acting in cartoons, and take a leave to travel a bit. Yakko dreamt to go to Pennsylvania, his Motherland and the Motherland of his ancestors. Wakko thought about Tasmania, its beaches, sun and sea. Dot chose Transylvania as the most mysterious and unpredictable place.
But to their great regret, the leave turned to be very short, so it will be enough to visit one of the three above named places. That's why Yakko, as the cleverest, came up with a truly genius idea: let each of the three roll an ordinary six-sided die, and the one with the highest amount of points will be the winner, and will take the other two to the place of his/her dreams.
Yakko thrown a die and got Y points, Wakko — W points. It was Dot's turn. But she didn't hurry. Dot wanted to know for sure what were her chances to visit Transylvania.
It is known that Yakko and Wakko are true gentlemen, that's why if they have the same amount of points with Dot, they will let Dot win.
- 约分即分子分母同时除以gcd
点击查看代码
int a,b,n,res,i=1;
    bool flag=false;
    cin>>a>>b;
    int fz=max(a,b);
    if(fz==6)cout<<"1/6",flag=true;
    if(fz==1)cout<<"1/1",flag=true;
    res=gcd(7-fz,6);
    if(!flag)cout<<(7-fz)/res<<"/"<<6/res;
C题 Station of Fate
There are nn people standing in mm stations, forming mm queues.You don't know which person is in which station, or in what order they are standing in queue, so you want to count the number of different schemes.Two schemes are considered different, if and only if there exists a station whose queue consists of different people, or has different orders.Calculate the number of different schemes modulo 998, 244, 353998244353.
- 
n个人排成m队,可以看做n个人随机排列,形成n-1个空位,插空法选择其中m-1个空位,即可将之分成m个不同顺序组合,随机排列有\(n!\)种做法,插空有种\(C^{m-1}_{n-1}\)种做法。 
- 
数据范围是1e5,所以我们可以先预处理阶乘数组以及阶乘数组的逆元数组(费马小定理配合快速幂求解),利用组合数公式\(C^m_n=\frac {n!}{(n-m)!m!}\)即可得出组合数,时间复杂度\(O(nlogn)\) 
点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5+10,mod=998244353;
int n,m,a,b,jc[N],ijc[N];
int qsm(int a,int k,int p){
    LL res=1;
    while(k){
        if(k&1)res=(LL)res*a%p;
        a=(LL)a*a%p;
        k>>=1;
    }
    return res;
}
LL c(int a,int b){
    return (LL)jc[a]*ijc[a-b]%mod*ijc[b]%mod;
}
int main(){
    cin>>n;
    jc[0]=ijc[0]=1;
    for (int i = 1; i < N; i ++ ){
        jc[i]=(LL)jc[i-1]*i%mod;
        ijc[i]=(LL)ijc[i-1]*qsm(i,mod-2,mod)%mod;
    }
    while(n--){
        cin>>a>>b;
        cout<<(LL)(jc[a]*c(a-1,b-1))%mod<<endl;
    }
    return 0;
}
D题 The last digit
Nestor was doing the work of his math class about three days but he is tired of make operations a lot and he should deliver his task tomorrow. His math’s teacher gives him two numbers a and b. The problem consist of finding the last digit of the potency of base a and index b. Help Nestor with his problem. You are given two integer numbers: the base a (0 <= a <= 20) and the index b (0 <= b <= 2,147,483,000), a and b both are not 0. You have to find the last digit of ab.
- 最后一位数字不断自乘并对10取模即可
点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5+10,mod=998244353;
int n,m,a,b,jc[N],ijc[N];
LL qsm(int a,int k,int p){
    LL res=1;
    while(k){
        if(k&1)res=(LL)res*a%p;
        a=(LL)a*a%p;
        k>>=1;
    }
    return res;
}
int main(){
    cin>>n;
    while(n--){
        cin>>a>>b;
        cout<<qsm(a,b,10)<<endl;
    }
    return 0;
}
E题 Big Mod
Calculate R := B P mod M for large values of B, P, and M using an efficient algorithm. (That’s right, this problem has a time dependency !!!.)
- 快速幂裸题
点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5+10,mod=998244353;
int n,m,a,b,jc[N],ijc[N];
LL qsm(int a,int k,int p){
    LL res=1;
    while(k){
        if(k&1)res=(LL)res*a%p;
        a=(LL)a*a%p;
        k>>=1;
    }
    return res;
}
int main(){
    while(cin>>a>>b>>m){
        cout<<qsm(a,b,m)<<endl;
    }
    return 0;
}
F题 乘法逆元 2
这可能是一道模板题。
给定 \(n\) 个正整数 \(ai\),求每个数在模 \(p\) 意义下的乘法逆元。
提示:请使用高效的读入方式。
输出要求:\(\sum_{i=1}^{n}a^{-1}_{i}×998244353^{n-i}(mod p)\)
- 
数据范围有点大,直接暴力对每个数求一遍逆元nlogn会超时(做不出来于是求助度娘了hhh) 
- 
令\(S=\prod_{i=1}^{n}a_i\),则\(ans=\frac{\sum_{i=1}^{n}k^{n-i}\frac{s}{a_i}}{S}\) 
- 
其中\(\frac{S}{a_i}\)可以看做前i-1个数的前缀积乘上后n-i个数的后缀积(预处理),于是只要对\(S\)求一次逆元即可,时间复杂度\(O(n)\) 
点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 5e6+10,mod=998244353;
int n,m,b,a[N],p=1e9+7;
ll qzj[N],hzj[N];
template <typename T>
inline void read(T &x)//快读
{
    char c;
    x = 0;
    int fu = 1;
    c = getchar();
    while(c > 57 || c < 48)
    {
        if(c == 45)
        {
            fu = -1;
        }
        c = getchar();
    }
    while(c <= 57 && c >= 48)
    {
        x = (x << 3) + (x << 1) + c - 48;
        c = getchar();
    }
    x *= fu;
}
template <typename T>
inline void fprint(T x)//快输
{
    if(x < 0)
    {
        putchar(45);
        x = -x;
    }
    if(x > 9)
    {
        fprint(x / 10);
    }
    putchar(x % 10 + 48);
}
ll qsm(int a,ll k,int p){
    ll res=1;
    while(k){
        if(k&1)res=(ll)res*a%p;
        a=(ll)a*a%p;
        k>>=1;
    }
    return res;
}
int main(){
	read(n);
	qzj[0]=1;
	hzj[n+1]=1;
	for(register int i=1;i<=n;i++){
		read(a[i]);
		qzj[i]=(ll)qzj[i-1]*a[i]%p;
	}
	for(register int i=n;i>=1;i--)
		hzj[i]=(ll)hzj[i+1]*a[i]%p;
		
	ll sa=0;
	ll tmp=1;
	ll inv=qsm(qzj[n],p-2,p);
	for(register int i=n;i>=1;i--){
		if(i!=n)
		tmp=(ll)tmp*mod%p;
		sa=(sa+(ll)(tmp*((ll)qzj[i-1]%p*hzj[i+1]%p)%p)%p)%p;
	}
	fprint((ll)sa*inv%p);
    return 0;
    
}
G题 Strange Functions
Let's define a function \(f(x)\) (x is a positive integer) as follows: write all digits of the decimal representation of x backwards, then get rid of the leading zeroes. For example, \(f(321) = 123,f(321)=123, f(120) = 21,f(120)=21, f(1000000) = 1,f(1000000)=1, f(111) = 111f(111)=111.\)
Let's define another function \(g(x) = \frac{x}{f(f(x))}g(x)= f(f(x))\)(\(x\) is a positive integer as well).
Your task is the following: for the given positive integer nn, calculate the number of different values of g(x) among all numbers x such that \(1≤x≤n\).
- 观察样例易猜得答案即输入数的位数(\(x\)每多一个非最高位0对应\(f(x)\)便多一种值)
点击查看代码
a=int(input())
while(a):
    b=input()
    print(len(b))
    a-=1
H题 Knight Moves
编写一个程序,计算一个骑士从棋盘上的一个格子到另一个格子所需的最小步数。
- 爆搜bfs即可,st数组存是否走到过,每个格子第一次时走到一定是最优解,故新遍历到的格子的步数值为上一步格子的步数值+1
点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
#define endl '\n'
using namespace std;
typedef pair<int,int> pii;
const int N = 310,mod=998244353;
int n,m,b,g[N][N];
bool st[N][N];
pii d[8] = {{2,-1},{2,1},{-2,1},{-2,-1},{1,2},{1,-2},{-1,2},{-1,-2}};
void bfs(pii sta,pii ed){
	queue<pii> q;
	q.push(sta);
	st[sta.x][sta.y]=true;
	if(sta==ed)return;
	while(q.size()){
		pii t=q.front();
		q.pop();
		for(int i=0;i<8;i++){
			int dx=d[i].x,dy=d[i].y,cx=t.x+dx,cy=t.y+dy;
			if(cx<n and cx>=0 and cy<n and cy>=0 and !st[cx][cy]){
				g[cx][cy]=g[t.x][t.y]+1;
				st[cx][cy]=true;
				q.push({cx,cy});
			}
			if(cx==ed.x and cy==ed.y)return;
		}
	}
}
int main(){
	int t;
	pii sta,ed;
	cin>>t;
	while(t--){
		memset(g,0,sizeof g);
		memset(st,0,sizeof st);
		cin>>n;
		cin>>sta.x>>sta.y;
		cin>>ed.x>>ed.y;
		bfs(sta,ed);
		cout<<g[ed.x][ed.y]<<endl;
	}
    return 0;
    
}
k题 Reverse Binary Strings
You are given a string ss of even length nn. String ss is binary, in other words, consists only of 0's and 1's.String ss has exactly \(\frac{n}{2}\) zeroes and \(\frac{n}{2}\) ones (n is even).
In one operation you can reverse any substring of ss. A substring of a string is a contiguous subsequence of that string.
What is the minimum number of operations you need to make string ss alternating? A string is alternating if \({s_i}!=s_{i+1}\)for all \(i\). There are two types of alternating strings in general: 01010101... or 10101010...
- 
观察易得答案为\(max(连续0的个数,连续1的个数)\) 
- 
证明:略 
点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
typedef long long LL;
const int N = 2e5+10,mod=998244353;
int n,m;
string a;
int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        cin>>a;
        int l1=0,ans0=0,l0=0,ans1=0;
        for (int i = 0; i < n; i ++ ){
            if(a[i]=='1'){
                l1+=1;
                if(l0>1)ans0+=l0-1;
                l0=0;
            }
            else{
                l0+=1;
                if(l1>1)ans1+=l1-1;
                l1=0;
            }
        }
        cout<<max(ans0,ans1)<<endl;
    }
    return 0;
}
O题 山峰和山谷 Ridges and Valleys
给定一个 n \times nn×n 的网格状地图,每个方格 (i,j)(i,j) 有一个高度 \(w_{ij}\)
。如果两个方格有公共顶点,则它们是相邻的。
定义山峰和山谷如下:
- 均由地图上的一个连通块组成;
- 所有方格高度都相同;
- 周围的方格(即不属于山峰或山谷但与山峰或山谷相邻的格子)高度均大于山谷的高度,或小于山峰的高度。
求地图内山峰和山谷的数量。特别地,如果整个地图方格的高度均相同,则整个地图既是一个山谷,也是一个山峰。
- bfs爆搜,开st数组记录当前格子是否被遍历过,每次bfs初始化山峰山谷两个布尔值,依次对每个格子周围八个格子进行判断
- 对于每次判断,如果周围有比当前格高的点,那当前格就不是山峰,如果有比当前格低的点,那当前格就不是山谷,如果高度和当前格相同,那和当前格就构成连通块,将之加入到bfs队列,并标记为遍历过
- 最后判断当前连通块是否是山峰山谷
点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define x first
#define y second
const int N = 1010,M=1010000;
int n,m,w[N][N];
bool st[N][N];
int dx[8] = {-1, -1, -1, 0, 1, 1, 1, 0};
int dy[8] = {-1, 0, 1, 1, 1, 0, -1, -1};
int maxs,mins;
queue<pair<int,int>> q;
void bfs(int a, int b){
    st[a][b]=true;
    queue<pair<int,int>> empty;
	swap(empty, q);
    int siz=0;
    q.push({a,b});
     bool fx = true,fs=true;
    while(q.size()){
        for (int i = 0; i < 8; i ++ ){
            int x=q.front().x,y=q.front().y;
            int a=dx[i]+q.front().x,b=dy[i]+q.front().y;
            if(a>=0 and b>=0 and a<n and b<n ){
           if(w[a][b]<w[x][y])
           {
               fs=false;
           }
           else if(w[a][b]>w[x][y])
           {
               fx=false;
           }
           else if(!st[a][b]){
                q.push({a,b});
                st[a][b]=true;
            }
            }
        }
        q.pop();
    }
   
   
    if(fx)maxs+=1;
    if(fs)mins++;
    
    return;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            cin>>w[i][j];
    
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            if(!st[i][j]){
                bfs(i,j);
            }
            
    cout<<maxs<<" "<<mins;
}
P题 Prime Ring Problem
A ring is composed of n (even number) circles as shown in diagram.
Put natural numbers 1, 2, . . . , n into each circle separately, and the
sum of numbers in two adjacent circles should be a prime.
Note: the number of first circle should always be 1.
- 深搜,开st数组记录当前数字是否用过,每次遍历该组合的下一位数字
- 每次搜索遍历\(1~n\)检查当前填入数字是否符合与上一位数字和为素数,遍历到最后一位检查当前数字与1的和是否为素数
- 恶心的输出格式(输出每组合法状态行末不能有空格,最后一组数据输出结束后不用换行)
点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define x first
#define y second
#define endl '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5+10,mod=998244353;
int n,m;
int pr[10]={3,5,7,11,13,17,19,23,29,31};//事后想想还是开一个函数判断素数更好写
bool st[18];
vector <vector<int>> ans;
void dfs(int u,vector<int> res){
	for(int i=2;i<=n;i++){
		if(!st[i]){
			if(u==n){
				for(int j=0;j<10;j++){
					if((i+res[u-1])==pr[j]){
						for(int k=0;k<10;k++){
							if(i+1==pr[k]){
								res[u]=i;
								ans.push_back(res);
								return;
							}
							if(k!=9 and i+1<pr[k+1])
								break;
						}
					}
					if(j!=9 and i+res[u-1]<pr[j+1]){
						return;
					}
				}
			}
			else{
				for(int j=0;j<10;j++){
					if((i+res[u-1])==pr[j]){
						st[i]=true;
						res[u]=i;
						dfs(u+1,res);
						st[i]=false;
						break;
					}
					if(j!=9 and i+res[u-1]<pr[j+1]){
						break;
					}
				}
			}
		}
	}
}
int main(){
	int k=1;
	while(cin>>n){
		if(k!=1)cout<<endl;
		printf("Case %d:\n",k);
		vector <int> sol(18,0);
		ans.clear();
		memset(st,0,sizeof st);
		sol[1]=1;
		st[1]=true;
		dfs(2,sol);
		for(int i=0;i<ans.size();i++){
			for(int j=1;j<n;j++){
				cout<<ans[i][j]<<" ";
			}
			cout<<ans[i][n];
			cout<<endl;
		}
		k++;
	}
    return 0;
    
}
R题 小木棍
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 5050 。现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
- 
爆搜加一点剪枝过了一半数据然后就怎么都不会于是就参考洛谷题解了。。。 
- 
剪枝: 
- 
木棍从大到小排序,如果先用短木棍,那么需要很多根连续的短木棍接上一根长木棍才能拼成一根原来的长棍,那么短木棍都用了后,剩下了大量长木棍,拼起来就不如短木棍灵活,更难接出原始长度。而先用长木棍,最后再用短木棍补刀,这样就剩下了相对较短的木棍,能更加灵活地拼接出原始长度。 
- 
当dfs返回拼接失败,需要更换当前使用的木棍时,不要再用与当前木棍的长度相同的木棍,因为当前木棍用了不行,改成与它相同长度的木棍一样不行。预处理出排序后每根木棍后面的最后一根与这根木棍长度相等的木棍(程序中的next数组),它的下一根木棍就是第一根长度不相等的木棍了。 
- 
用st数组标记每根木棍是否用过。 
- 
二分找出第一个木棍长度不大于未拼长度rest。它后面的木棍一定都满足这个条件。 
- 
发现答案后直接exit(0)。 
- 
如果当前长棍剩余的未拼长度等于当前木棍的长度或原始长度,继续拼下去时却失败了,就直接回溯。 
点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int N = 70;
int n,m,a[N],nex[N],len;
bool st[N];
bool flag=false;
bool cmp(int a,int b){
	return a>b;
}
inline int read(){
    int x=0; bool f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
    for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
    if(f) return x;
    return 0-x;
}
void check(int u,int cur,int num,int last){//dfs爆搜 ,状态数组st,已经用了u位数字,当前总和为cur,目标为num,上一根木棍k 
    if(u==n and cur==0){
    	printf("%d",num);
    	exit(0);
	}
	if(num-cur<a[n-1])return;
	if(cur==0){
		int k=0;
		for(int i=k;i<n;i++)
			if(!st[i]){
				k=i;
				st[i]=true;
				break;
			}
		check(u+1,cur+a[k],num,k);
		st[k]=false;
		return;
	}
	int l=last+1, r=n, mid;
    while(l<r){
        mid=(l+r)>>1;
        if(a[mid]<=(num-cur)) r=mid;
        else l=mid+1;
    }
	for(register int i=l;i<n;i++){
		if(!st[i]){
			if(!st[i] and cur+a[i]==num){
				st[i]=true;
				check(u+1,0,num,i);
				st[i]=false;
				return;
			}
			else if(!st[i] and cur+a[i]<num){
				st[i]=true;
				check(u+1,cur+a[i],num,i);
				st[i]=false;
			}
			if(num-cur==len or i==n-1)return;
			i=nex[i];
		}
	}
	
}
int main()
{
    scanf("%d",&n);
    int sum=0,x,maxs,cnt=0;
    for (int i = 0; i < n; i ++ ){
        x=read();
        if(x<=50){
   			a[cnt++]=x;
   	 		sum+=x;
    		maxs=max(maxs,x);
    	}
    }
    n=cnt;
    sort(a,a+n,cmp);
    nex[n]=n;
    for(int i=n-1;i>=0;i--){
        if(a[i]==a[i+1]) nex[i]=nex[i+1];
        else nex[i]=i;
    }
    for (int i = maxs; i <=sum>>1; i ++ ){
        if(sum%i==0){
        	len=a[i];
			check(0,0,i,0); 
            }
        }
    printf("%d",sum);
    return 0;
}
第一次训练赛
C题 Igor and his way to work
Woken up by the alarm clock Igor the financial analyst hurried up to the work. He ate his breakfast and sat in his car. Sadly, when he opened his GPS navigator, he found that some of the roads in Bankopolis, the city where he lives, are closed due to road works. Moreover, Igor has some problems with the steering wheel, so he can make no more than two turns on his way to his office in bank.
Bankopolis looks like a grid of n rows and m columns. Igor should find a way from his home to the bank that has no more than two turns and doesn't contain cells with road works, or determine that it is impossible and he should work from home. A turn is a change in movement direction. Igor's car can only move to the left, to the right, upwards and downwards. Initially Igor can choose any direction. Igor is still sleepy, so you should help him.
- 
dfs,递归传递变量方向以及方向改变次数 
- 
开一个vis数组记录到达当前格子所需的最小转向次数,如果重复走到某个格子就更新vis数组或结束递归 
- 
比赛的时候没想到vis数组应该记录最小转向次数,写成记录是否到达,最后寄了 
点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
#define x first
#define y second
 
using namespace std;
typedef pair<int,int> pii;
const int N = 1010;
 
int n,m,g[N][N],vis[N][N][4];
bool st[N][N];
pii sta,ed;
pii d[4]={{0,1},{-1,0},{1,0},{0,-1}};
bool flag=false;
 
void dfs(int dir,int change,pii cur){
	if(change>2)return;
	if(cur==ed and change<=2){
		cout<<"YES";
		exit(0);
	}
	if(vis[cur.x][cur.y][dir]<=change)return;
	vis[cur.x][cur.y][dir]=change;
	for(int i=0;i<4;i++){
		int cx=cur.x+d[i].x,cy=cur.y+d[i].y;
		if(cx<n and cy<m and cx>=0 and cy>=0 and !st[cx][cy] and g[cx][cy]!=0){
			//st[cx][cy]=true;
			if(dir!=i){
				dfs(i,change+1,{cx,cy});
				//st[cx][cy]=false;
			}
			else{
				dfs(i,change,{cx,cy});
				//st[cx][cy]=false;
			}
		}
	}
}
 
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
    cin>>n>>m;
    char c;
    bool flag=false;
    for(int i=0;i<n;i++){
    	for(int j=0;j<m;j++){
    		cin>>c;
    		if(c=='.')g[i][j]=1;
    		else if(c=='*')g[i][j]==0;
    		else if(c=='S')sta.x=i,sta.y=j;
    		else ed.x=i,ed.y=j,g[i][j]=1;
		}
	}
 
	memset(vis,0x1f1f1f,sizeof vis);
	for(int i=0;i<4;i++){
		memset(st,0,sizeof st);
		//st[sta.x][sta.y]=true;
		dfs(i,0,sta);
		//st[sta.x][sta.y]=false;
	}
	cout<<"NO";
    return 0;
}
第二次训练赛
C题 Convex Quadrilateral
Consider a two-dimensional coordinate plane, where the x-axis is oriented to the right, and the y-axis is oriented upward.In this plane, there is a quadrilateral without self-intersection.The coordinates of the four vertices are (Ax,A y), (Bx,By), (Cx,Cy), and (Dx,Dy), in counter-clockwise order.Determine whether this quadrilateral is convex.Here, a quadrilateral is convex if and only if all four interior angles are less than 180 degrees.
- 
我们可以连接两条对角线然后对每条对角线进行判断,对于每条对角线来说另外两个定点是否都位于对角线两侧,如果对于两条对角线来说另外两个点全部位于对角线两侧那么这就是个凸四边形 
- 
在点到直线的距离公式中,如果我们去掉绝对值,那么就可以根据距离的正负判断该点在直线的哪一侧,由此便可以判断两点是否分居对角线两侧 
- 
倘若对角线L线垂直于x轴,我们只要判断另外两个点是否分居L两侧即可 
- 
比赛的时候智障了把四边形的边当对角线传参了debug半天没找出问题 
点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
#define x first
#define y second
 
using namespace std;
typedef pair<int,int> pii;
const int N = 1010;
 
int n,m,g[N][N],vis[N][N][4];
long double k,b;
bool dist(int x1,int y1,int x2,int y2){
	long double dist1=k*x1-y1+b,dist2=k*x2-y2+b;
	if(dist1>=0 and dist2>=0)return false;
	if(dist1<0 and dist2<0)return false;
	return true;
}
void zx(int x1,int y1,int x2,int y2){
	
	k=(float)(y2-y1)/(x2-x1);
	b=(float)y1-k*x1;
}
 
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int x1,y1,x2,y2,x3,y3,x4,y4;
	
    cin>>x1>>y1;
    cin>>x2>>y2;
    cin>>x3>>y3;
    cin>>x4>>y4;
    if(x1==x3){
    	if(x2==x4){
    		cout<<"Yes";
			exit(0);
		}
    	else{
    		swap(x1,x2);
    		swap(y1,y2);
    		swap(x3,x4);
    		swap(y3,y4);
		}
	}
    zx(x1,y1,x3,y3);
    if(dist(x2,y2,x4,y4)){
    	if(x2==x4){
    		if(x1>x2 and x3>x2){
    			cout<<"No";
    			return 0;
			}
			else if(x1<x2 and x3<x2){
				cout<<"No";
    			return 0;
			}
			else{
				cout<<"Yes";
    			return 0;
			}
		}
    	zx(x2,y2,x4,y4);
    	if(dist(x1,y1,x3,y3))
    		cout<<"Yes";
    	else
    		cout<<"No";
	}else cout<<"No";
    return 0;
}
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号