2023冲刺清北营11
T1 一瓶消毒液
由于数据范围是 \(200\) 而不是 \(1000\) ,因此可以直接 \(O(n^3)\) 暴力枚举判断,假设当前枚举 \(i,j,k\) ,考虑判断移动 \(k\) 线段是否能使 \(i,j\) 联通,此时我们的问题变成了求解两条线段的最短距离。
首先判断线段相交的情况,容易发现这等价于选择一条线段为分割线,判断另一个线段的两个端点位于该线段所在直线的两侧,可以简单使用向量叉积进行判断,由于是线段相交,因此每条线段都要判一次。
此时考虑求解线段距离,考虑通过几种情况简单取 \(\min\) 得到答案:首先线段端点进行组合构成线段,其次线段端点向另一条线段做垂线构成线段。考虑判断一个点关于一条线段的垂线是否在线段上,由于不在线段上时线段与点构成钝角三角形,因此可以使用向量点积进行判断。之后通过三角形的面积可以求解点到线段的距离。
然而此题严重卡精,因此使用 __int128_t 进行运算,此时距离公式无法进行开根,因此计算点到直线的距离的公式为 \(\tfrac{S^2}{dis}\) ,然而 \(S^2\) 会使得 __int128_t 溢出,因此上述计算使用龟速乘解决,与求解 \(A\times B\operatorname{mod} P\) 写法类似。
code
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <bitset>
using namespace std;
const int max1 = 242;
const __int128_t inf = 1e38;
int T, n;
struct Point
{
long long x, y;
Point () {}
Point ( long long __x, long long __y )
{ x = __x, y = __y; }
__int128_t operator * ( const Point &A ) const
{ return (__int128_t) x * A.y - (__int128_t) y * A.x; }
__int128_t operator & ( const Point &A ) const
{ return (__int128_t) x * A.x + (__int128_t) y * A.y; }
};
__int128_t Get_Dis ( const Point &A, const Point &B )
{
return (__int128_t) ( A.x - B.x ) * ( A.x - B.x ) + (__int128_t) ( A.y - B.y ) * ( A.y - B.y );
}
struct Line
{
Point A, B; __int128_t dis;
Line () {}
Line ( Point __A, Point __B, __int128_t __dis )
{ A = __A, B = __B, dis = __dis; }
bool operator < ( const Line &A ) const
{ return dis < A.dis; }
bool operator < ( const __int128_t &A )
{ return dis < A; }
};
Line L[max1 + 5];
__int128_t Across ( const Point &A, const Point &B, const Point &C )
{
return Point(B.x - A.x, B.y - A.y) * Point(C.x - A.x, C.y - A.y);
}
__int128_t Cross ( const Point &A, const Point &B, const Point &C )
{
return Point(B.x - A.x, B.y - A.y) & Point(C.x - A.x, C.y - A.y);
}
__int128_t Quick_Mul ( __int128_t A, __int128_t B, __int128_t mod )
{
__int128_t k1 = A / mod, k2 = 0, val = 0; A %= mod;
while ( B )
{
if ( B & 1 )
{
k2 += k1;
val += A;
k2 += val / mod;
val %= mod;
}
B >>= 1;
k1 = k1 + k1 + ( A + A ) / mod;
A = ( A + A ) % mod;
}
if ( val )
++k2;
return k2;
}
__int128_t Get_Dis ( const Line &P, const Point &Q )
{
__int128_t S = Across(Q, P.A, P.B);
if ( S < 0 )
S = -S;
return Quick_Mul(S, S, Get_Dis(P.A, P.B));
}
__int128_t Get_Dis ( const Line &P, const Line &Q )
{
int p1 = Across(P.A, P.B, Q.A) >= 0 ? 1 : -1, p2 = Across(P.A, P.B, Q.B) >= 0 ? 1 : -1;
int q1 = Across(Q.A, Q.B, P.A) >= 0 ? 1 : -1, q2 = Across(Q.A, Q.B, P.B) >= 0 ? 1 : -1;
if ( p1 * p2 < 0 && q1 * q2 < 0 )
return 0;
__int128_t ans = inf;
if ( Cross(P.A, P.B, Q.A) > 0 && Cross(P.B, P.A, Q.A) > 0 )
ans = min(ans, Get_Dis(P, Q.A));
if ( Cross(P.A, P.B, Q.B) > 0 && Cross(P.B, P.A, Q.B) > 0 )
ans = min(ans, Get_Dis(P, Q.B));
if ( Cross(Q.A, Q.B, P.A) > 0 && Cross(Q.B, Q.A, P.A) > 0 )
ans = min(ans, Get_Dis(Q, P.A));
if ( Cross(Q.A, Q.B, P.B) > 0 && Cross(Q.B, Q.A, P.B) > 0 )
ans = min(ans, Get_Dis(Q, P.B));
ans = min(ans, Get_Dis(P.A, Q.A));
ans = min(ans, Get_Dis(P.A, Q.B));
ans = min(ans, Get_Dis(P.B, Q.A));
ans = min(ans, Get_Dis(P.B, Q.B));
return ans;
}
bitset <max1 + 5> bit[max1 + 5][max1 + 5];
void Work ()
{
scanf("%d", &n);
for ( int i = 1; i <= n; i ++ )
{
scanf("%lld%lld%lld%lld", &L[i].A.x, &L[i].A.y, &L[i].B.x, &L[i].B.y);
L[i].dis = Get_Dis(L[i].A, L[i].B);
}
sort(L + 1, L + 1 + n);
for ( int i = 1; i <= n; i ++ )
for ( int j = i + 1; j <= n; j ++ )
bit[i][j].reset();
for ( int i = 1; i <= n; i ++ )
{
for ( int j = i + 1; j <= n; j ++ )
{
__int128_t dis = Get_Dis(L[i], L[j]);
int pos = lower_bound(L + 1, L + 1 + n, dis) - L;
for ( int k = pos; k <= n; k ++ )
{
if ( i == k || j == k )
continue;
if ( k < i )
bit[k][i][j] = 1;
else if ( k < j )
bit[i][k][j] = 1;
else
bit[i][j][k] = 1;
}
}
}
int ans = 0;
for ( int i = 1; i <= n; i ++ )
for ( int j = i + 1; j <= n; j ++ )
ans += bit[i][j].count();
printf("%d\n", ans);
return;
}
int main ()
{
freopen("dis.in", "r", stdin);
freopen("dis.out", "w", stdout);
scanf("%d", &T);
while ( T -- )
Work();
return 0;
}
T2 一张桌子
简单推一下式子:
发现这是一个叉积的形式,等价于线段 \(XY\) 到线段 \(YZ\) 的过程为向左弯曲,因此直接对整张图每一层求解凸包,然后简单合并即可。
code
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int max1 = 3000;
const int inf = 0x3f3f3f3f;
struct Point
{
int x, y;
vector <int> id;
Point () {}
Point ( int __x, int __y )
{ x = __x, y = __y; }
long long operator * ( const Point &A ) const
{ return 1LL * x * A.y - 1LL * A.x * y; }
bool operator < ( const Point &A ) const
{
if ( x == A.x )
return y < A.y;
return x < A.x;
}
bool operator != ( const Point &A ) const
{ return x != A.x || y != A.y; }
};
int n, len, p[max1 + 5], s[max1 + 5], top;
bool vis[max1 + 5]; Point A[max1 + 5], B[max1 + 5];
long long Across ( const Point &A, const Point &B, const Point &C )
{ return Point(B.x - A.x, B.y - A.y) * Point(C.x - A.x, C.y - A.y); }
int main ()
{
freopen("table.in", "r", stdin);
freopen("table.out", "w", stdout);
scanf("%d", &n);
for ( int i = 1; i <= n; i ++ )
{
scanf("%d%d", &A[i].x, &A[i].y);
A[i].id.clear();
A[i].id.push_back(i);
}
sort(A + 1, A + 1 + n);
len = 0; B[0] = Point(-inf, -inf);
for ( int i = 1; i <= n; i ++ )
{
if ( !len || B[len] != A[i] )
B[++len] = A[i];
else
B[len].id.push_back(A[i].id.back());
}
p[0] = 0; int now = 0;
for ( int T = 1; T <= len; T ++ )
{
top = 0;
s[++top] = p[now];
for ( int i = 1; i <= len; i ++ )
{
if ( vis[i] )
continue;
while ( top > 1 && Across(B[s[top]], B[i], B[s[top - 1]]) < 0 )
--top;
s[++top] = i;
}
for ( int i = 2; i <= top; i ++ )
p[++now] = s[i], vis[s[i]] = true;
top = 0;
s[++top] = p[now];
for ( int i = len; i >= 1; i -- )
{
if ( vis[i] )
continue;
while ( top > 1 && Across(B[s[top]], B[i], B[s[top - 1]]) < 0 )
--top;
s[++top] = i;
}
for ( int i = 2; i <= top; i ++ )
p[++now] = s[i], vis[s[i]] = true;
if ( now == n )
break;
}
for ( int i = 1; i <= n; i ++ )
for ( auto v : B[p[i]].id )
printf("%d ", v);
printf("\n");
return 0;
}
T3 11:23
题解说手玩即可……
然而我太废物了根本不会玩。
大家有兴趣的去玩一下吧!
简单写了一个模拟器,可以判断不合法字符,房屋选址重复,房屋相邻,剩余资源不够的情况,道路相邻的判断比较麻烦,没有写。
code
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
const int max1 = 10, max2 = 1e4;
int n, m, S[max1 + 5][max1 + 5], D[max1 + 5][max1 + 5];
int h1, h2, h3, h4, l1, l2, l3, l4, a1, a2, a3, a4;
int K, G, V[max2 + 5];
int kind[max1 + 5][max1 + 5], road[2][max1 + 5][max1 + 5];
int day, now1, now2, now3, now4;
struct Option
{
char opt; int A, B, C, D;
Option () {}
Option ( char __opt, int __A, int __B, int __C, int __D )
{ opt = __opt, A = __A, B = __B, C = __C, D = __D; }
};
vector <Option> ans;
void PrintAll ()
{
printf("\n\n\n\n\n\n\n\n\n\n");
printf("Day %d Number %d K %d\n", day, V[day], K);
printf("\n\n");
printf("Resource : %d %d %d %d\n", now1, now2, now3, now4);
printf("\n\n");
printf("house cost : %d %d %d %d\n", h1, h2, h3, h4);
printf("up cost : %d %d %d %d\n", l1, l2, l3, l4);
printf("road cost : %d %d %d %d\n", a1, a2, a3, a4);
printf("\n\n");
printf("Future : ");
for ( int i = day + 1; i <= G; i ++ )
printf("%d ", V[i]);
printf("\n");
int count = 0;
for ( int i = 0; i <= n; i ++ )
for ( int j = 0; j <= m; j ++ )
count += kind[i][j];
printf("count = %d\n", count);
printf("-------");
for ( int i = 0; i <= m + m; i ++ )
printf("-------");
printf("\n");
printf("| |");
for ( int i = 0; i <= m + m; i ++ )
{
if ( i & 1 )
printf("| |");
else
{
if ( ( i >> 1 ) / 10 )
printf("| |");
else
printf("| |");
}
}
printf("\n");
printf("| |");
for ( int i = 0; i <= m + m; i ++ )
{
if ( i & 1 )
printf("| |");
else
{
if ( ( i >> 1 ) / 10 )
printf("| %d |", i >> 1);
else
printf("| %d |", i >> 1);
}
}
printf("\n");
printf("| |");
for ( int i = 0; i <= m + m; i ++ )
{
if ( i & 1 )
printf("| |");
else
{
if ( ( i >> 1 ) / 10 )
printf("| |");
else
printf("| |");
}
}
printf("\n");
for ( int i = 0; i <= n + n; i ++ )
{
printf("-------");
for ( int k = 0; k <= m + m; k ++ )
printf("-------");
printf("\n");
printf("| |");
for ( int j = 0; j <= m + m; j ++ )
printf("| |");
printf("\n");
if ( i & 1 )
printf("| |");
else
{
if ( ( i >> 1 ) / 10 )
printf("| %d |", i >> 1);
else
printf("| %d |", i >> 1);
}
for ( int j = 0; j <= m + m; j ++ )
{
if ( i & 1 )
{
if ( j & 1 )
{
printf("|[%d,%d]|", S[i + 1 >> 1][j + 1 >> 1], D[i + 1 >> 1][j + 1 >> 1]);
}
else
{
if ( road[0][i >> 1][j >> 1] )
printf("|+++++|");
else
printf("| |");
}
}
else
{
if ( j & 1 )
{
if ( road[1][i >> 1][j >> 1] )
printf("|+++++|");
else
printf("| |");
}
else
{
if ( !kind[i >> 1][j >> 1] )
printf("| |");
else if ( kind[i >> 1][j >> 1] == 1 )
printf("| H |");
else
printf("| C |");
}
}
}
printf("\n");
printf("| |");
for ( int j = 0; j <= m + m; j ++ )
printf("| |");
printf("\n");
}
printf("-------");
for ( int i = 0; i <= m + m; i ++ )
printf("-------");
printf("\n\n\n\n");
return;
}
bool Check ( int A, int B, int C, int D )
{
return now1 >= A && now2 >= B && now3 >= C && now4 >= D;
}
int main ()
{
scanf("%d%d", &n, &m);
for ( int i = 1; i <= n; i ++ )
for ( int j = 1; j <= m; j ++ )
scanf("%d", &S[i][j]);
for ( int i = 1; i <= n; i ++ )
for ( int j = 1; j <= m; j ++ )
scanf("%d", &D[i][j]);
scanf("%d%d%d%d", &h1, &h2, &h3, &h4);
scanf("%d%d%d%d", &l1, &l2, &l3, &l4);
scanf("%d%d%d%d", &a1, &a2, &a3, &a4);
scanf("%d%d", &K, &G);
for ( int i = 1; i <= G; i ++ )
scanf("%d", &V[i]);
PrintAll();
printf("Please find a position to start.\n");
printf("\n\n\n\n");
int x, y;
scanf("%d%d", &x, &y);
kind[x][y] = 1;
ans.push_back(Option('B', x, y, 0, 0));
ans.push_back(Option('E', 0, 0, 0, 0));
now1 = now2 = now3 = now4 = 0;
for ( day = 1; day <= G; day ++ )
{
for ( int i = 1; i <= n; i ++ )
{
for ( int j = 1; j <= m; j ++ )
{
if ( D[i][j] == V[day] )
{
if ( S[i][j] == 1 )
{
now1 += kind[i - 1][j - 1];
now1 += kind[i - 1][j];
now1 += kind[i][j - 1];
now1 += kind[i][j];
}
else if ( S[i][j] == 2 )
{
now2 += kind[i - 1][j - 1];
now2 += kind[i - 1][j];
now2 += kind[i][j - 1];
now2 += kind[i][j];
}
else if ( S[i][j] == 3 )
{
now3 += kind[i - 1][j - 1];
now3 += kind[i - 1][j];
now3 += kind[i][j - 1];
now3 += kind[i][j];
}
else
{
now4 += kind[i - 1][j - 1];
now4 += kind[i - 1][j];
now4 += kind[i][j - 1];
now4 += kind[i][j];
}
}
}
}
char opt[3];
int A, B, C, D;
while ( true )
{
PrintAll();
scanf("%s", opt);
if ( opt[0] == 'E' )
{
ans.push_back(Option('E', 0, 0, 0, 0));
break;
}
else if ( opt[0] == 'B' )
{
scanf("%d%d", &A, &B);
if ( Check(h1, h2, h3, h4) )
{
if ( kind[A][B] || ( A > 1 && kind[A - 1][B] ) || ( A < n && kind[A + 1][B] ) || ( B > 1 && kind[A][B - 1] ) || ( B < m && kind[A][B + 1] ) )
printf("warning : There is a building in this place!\n");
else
{
kind[A][B] = 1;
now1 -= h1;
now2 -= h2;
now3 -= h3;
now4 -= h4;
ans.push_back(Option('B', A, B, 0, 0));
}
}
else
printf("Money is not enough.\n");
}
else if ( opt[0] == 'U' )
{
scanf("%d%d", &A, &B);
if ( Check(l1, l2, l3, l4) )
{
if ( kind[A][B] != 1 )
printf("warning : This is not a simple house!\n");
else
{
++kind[A][B];
now1 -= l1;
now2 -= l2;
now3 -= l3;
now4 -= l4;
ans.push_back(Option('U', A, B, 0, 0));
}
}
else
printf("Money is not enough.\n");
}
else if ( opt[0] == 'C' )
{
scanf("%d%d", &A, &B);
if ( Check(h1 + l1, h2 + l2, h3 + l3, h4 + l4) )
{
if ( kind[A][B] || ( A > 1 && kind[A - 1][B] ) || ( A < n && kind[A + 1][B] ) || ( B > 1 && kind[A][B - 1] ) || ( B < m && kind[A][B + 1] ))
printf("warning : There is a building in this place!\n");
else
{
kind[A][B] = 2;
now1 -= h1 + l1;
now2 -= h2 + l2;
now3 -= h3 + l3;
now4 -= h4 + l4;
ans.push_back(Option('C', A, B, 0, 0));
}
}
else
printf("Money is not enough.\n");
}
else if ( opt[0] == 'R' )
{
scanf("%d%d%d%d", &A, &B, &C, &D);
if ( Check(a1, a2, a3, a4) )
{
if ( A == C )
{
if ( B == D - 1 )
{
road[1][A][B] = 1;
now1 -= a1;
now2 -= a2;
now3 -= a3;
now4 -= a4;
ans.push_back(Option('R', A, B, C, D));
}
else if ( B == D + 1 )
{
road[1][A][D] = 1;
now1 -= a1;
now2 -= a2;
now3 -= a3;
now4 -= a4;
ans.push_back(Option('R', A, B, C, D));
}
else
printf("warning : This is an illegal option !\n");
}
else if ( B == D )
{
if ( A == C - 1 )
{
road[0][A][B] = 1;
now1 -= a1;
now2 -= a2;
now3 -= a3;
now4 -= a4;
ans.push_back(Option('R', A, B, C, D));
}
else if ( A == C + 1 )
{
road[0][C][B] = 1;
now1 -= a1;
now2 -= a2;
now3 -= a3;
now4 -= a4;
ans.push_back(Option('R', A, B, C, D));
}
else
printf("warning : This is an illegal option !\n");
}
else
printf("warning : This is an illegal option !\n");
}
else
printf("Money is not enough.\n");
}
else if ( opt[0] == 'X' )
{
scanf("%d%d", &A, &B);
bool flag = true;
if ( A == 1 && now1 < K )
flag = false;
if ( A == 2 && now2 < K )
flag = false;
if ( A == 3 && now3 < K )
flag = false;
if ( A == 4 && now4 < K )
flag = false;
if ( !flag )
printf("Money is not enough.\n");
else
{
if ( A == 1 )
now1 -= K;
if ( A == 2 )
now2 -= K;
if ( A == 3 )
now3 -= K;
if ( A == 4 )
now4 -= K;
if ( B == 1 )
++now1;
if ( B == 2 )
++now2;
if ( B == 3 )
++now3;
if ( B == 4 )
++now4;
ans.push_back(Option('X', A, B, 0, 0));
}
}
else
printf("warning : This is an illegal option !\n");
}
int count = 0;
for ( int i = 0; i <= n; i ++ )
for ( int j = 0; j <= m; j ++ )
count += kind[i][j];
if ( count >= 10 )
{
printf("You win !\n");
printf("%d\n", day);
for ( auto v : ans )
{
if ( v.opt == 'E' )
printf("%c\n", v.opt);
else if ( v.opt == 'R' )
printf("%c %d %d %d %d\n", v.opt, v.A, v.B, v.C, v.D);
else
printf("%c %d %d\n", v.opt, v.A, v.B);
}
return 0;
}
}
printf("You Fail!");
return 0;
}