CCPC-Wannafly Winter Camp Day5 (Div2, onsite)
Special Judge
题目描述
有一个nn个点mm条边的图画在了平面上,你想知道有多少对边之间对应的线段相交。
特别地,对于图中的一对边,如果有公共点且只在对应的端点相交,那么我们不认为这对边相交。
输入描述
第一行两个整数n, m(1\leq n\leq 1000, 1\leq m\leq 2000)n,m(1≤n≤1000,1≤m≤2000),表示点数和边数。
接下来mm行,每行两个整数(u,v)(u,v)表示一条uu与vv之间的无向边,保证图中没有重边和自环。
接下来nn行,每行两个整数x_i, y_i (0\leq x_i, y_i\leq 10^9)xi,yi(0≤xi,yi≤109)表示图中第ii个顶点的坐标,保证所有的坐标两两不同。
输出描述
输出一个整数,表示答案。
样例输入 1
4 6 1 2 1 3 1 4 2 3 2 4 3 4 0 0 0 1 1 1 1 0
样例输出 1
1
计算几何,先判是在同一边,再判不严格相交,注意叉积相乘会爆long long
#include <bits/stdc++.h>
#define maxn 1005
#define db double
using namespace std;
int n,m;
const db EPS=1e-9;
bool Mp[maxn][maxn];
vector<pair<int,int>> f;
typedef long long ll;
struct P
{
ll x,y;
int id;
P(){}
P(ll _x,ll _y):x(_x),y(_y) {}
P operator +(P p)
{
return {x+p.x,y+p.y};
}
P operator -(P p)
{
return {x-p.x,y-p.y};
}
P operator *(ll d)
{
return {x*d,y*d};
}
P operator /(ll d)
{
return {x/d,y/d};
}
ll dot(P p)
{
return x*p.x+y*p.y;
}
ll det(P p)
{
return x*p.y-y*p.x;
}
}pos[maxn];
ll cross(P p1,P p2,P p3)
{
return ((p2.x-p1.x)*(p3.y-p1.y)-(p3.x-p1.x)*(p2.y-p1.y));
}
ll sign(ll x,ll y)
{
if(x<0&&y>0) return -1;
else if(x>0&&y<0) return -1;
else if(x==0||y==0) return 0;
return 1;
}
bool intersect(ll l1,ll r1,ll l2,ll r2)
{
if(l1>r1) swap(l1,r1);
if(l2>r2) swap(l2,r2);
return !(r1<l2||r2<l1);
}
bool isSS(P p1,P p2,P q1,P q2)
{
return intersect(p1.x,p2.x,q1.x,q2.x)&&intersect(p1.y,p2.y,q1.y,q2.y)&&sign(cross(p1,p2,q1),cross(p1,p2,q2))<=0&&sign(cross(q1,q2,p1),cross(q1,q2,p2))<=0;
}
bool check(int xx,int yy,int zz,int dd)
{
if(xx==zz||xx==dd) return true;
if(yy==zz||yy==dd) return true;
return false;
}
bool Onseg(P p1,P p2,P q)
{
ll tmp=(q.x-p1.x)*(p2.y-p1.y)-((p2.x-p1.x)*(q.y-p1.y));
if(tmp==0)
{
ll min_x=min(p1.x,p2.x);
ll max_x=max(p1.x,p2.x);
ll min_y=min(p1.y,p2.y);
ll max_y=max(p1.y,p2.y);
if(min_x<=q.x&&q.x<=max_x&&min_y<=q.y&&q.y<=max_y) return true;
}
return false;
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
for(int i=0;i<m;++i)
{
int u,v;
scanf("%d%d",&u,&v);
Mp[u][v]=Mp[v][u]=true;
f.push_back(make_pair(u,v));
}
//if(Onseg(temp,tempp,zz)) cout<<"QAQ"<<endl;
for(int i=1;i<=n;++i)
{
scanf("%lld%lld",&pos[i].x,&pos[i].y);
}
int cnt=0;
for(int i=0;i<f.size();++i)
{
for(int j=i+1;j<f.size();++j)
{
P p_1=pos[f[i].first];
P p_2=pos[f[i].second];
P p_3=pos[f[j].first];
P p_4=pos[f[j].second];
int u_1=f[i].first;
int v_1=f[i].second;
int u_2=f[j].first;
int v_2=f[j].second;
if(Onseg(p_1,p_2,p_3)&&Onseg(p_1,p_2,p_4))
{
cnt++;
}
else if(Onseg(p_3,p_4,p_1)&&Onseg(p_3,p_4,p_2)) cnt++;
else if(check(u_1,v_1,u_2,v_2)) continue;
else if(isSS(p_1,p_2,p_3,p_4)) ++cnt;
}
}
cout<<cnt<<endl;
return 0;
}
Kropki
题目描述
你有一个11到nn的排列p_1, p_2, \dots, p_np1,p2,…,pn,对于所有的i (1\leq i\leq n-1)i(1≤i≤n−1),如果p_ipi和p_{i+1}pi+1中,有一个数是另一个的两倍,那么会在这两个数之间画上一个点,否则不会。
现在你把所有数字都擦掉了,只剩下了这些点,请问有多少种11到nn的排列满足条件。
输入描述
第一行一个正整数n (2\leq n\leq 15)n(2≤n≤15)。
接下来一行一个长度为n-1n−1的0101串,其中第ii个字符为11表示第ii个数与第i+1i+1个数之间有点,否则没有。
输出描述
输出一个答案,由于答案可能很大,对10^9+7109+7取模。
样例输入 1
4 001
样例输出 1
6
Div2 版本,状压即可。 dp[i][j]表示已经用了哪些数,最后一位数是什么,根据定义转移即可。(两种情况,最后一位是0,最后一位不是0)
#include <bits/stdc++.h>
#define maxn 20
using namespace std;
typedef long long ll;
char s[maxn];
int Mp[maxn][maxn];
ll dp[1<<20][maxn];
const ll mod=1e9+7;
int main()
{
int n;
scanf("%d",&n);
scanf("%s",s+1);
for(int i=0;i<(1<<n);++i)
{
int len=0;
for(int j=0;j<n;++j)
{
if(i&(1<<j)) ++len;
}
if(len==1)
{
for(int j=0;j<n;++j)
{
if(i&(1<<j))
{
dp[i][j+1]=1;
}
}
}
}
for(int i=1;i<(1<<n);++i)
{
int len=0;
for(int j=0;j<n;++j)
{
if(i&(1<<j)) ++len;
}
if(len==1)
{
continue;
}
len--;
if(s[len]=='0')
{
for(int j=0;j<n;++j)
{
if(i&(1<<j))
{
for(int k=0;k<n;++k)
{
if((j+1)==2*(k+1)||(k+1)==2*(j+1)) continue;
if((i&(1<<k))&&k!=j)
{
dp[i][j+1]=(dp[i][j+1]+dp[i^(1<<j)][k+1])%mod;
}
}
}
}
}
else
{
for(int j=0;j<n;++j)
{
if(i&(1<<j))
{
for(int k=0;k<n;++k)
{
if(k==j) continue;
if(i&(1<<k))
{
if((k+1)==2*(j+1)||(j+1)==2*(k+1))
{
dp[i][j+1]=(dp[i][j+1]+dp[i^(1<<j)][k+1])%mod;
}
}
}
}
}
}
}
ll ans=0;
for(int i=1;i<=n;++i)
{
ans=(ans+dp[(1<<n)-1][i])%mod;
}
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号