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