P4468题解

题目链接

博客

思路

观察到 \(n\) 最大只有 \(8\) ,可以考虑爆搜。我们可以考虑将折好的纸展开。显然,展开成最开始的正方形纸片后,纸片上有多少孔,那么最开始打的孔就穿过了多少层。

至于怎么展开,考虑将每个孔关于每个折痕求对称点,对称点便是展开一层后点的位置(显然应该按照折纸的顺序倒序展开)。由于题面中说折纸时为从右向左折,那么展开时只能从左向右展开,我们可以使用向量的叉积运算判断每个孔关于折痕的位置,如果在左边就继续递归这个孔和这个孔关于折痕的对称点(展开后孔的位置),递归 \(n\) 次后(即展开 \(n\) 次)后便可以得到完全展开后所有孔的位置,最后统计一下有多少个孔即可,注意一下判断最后的孔是否在正方形纸片的范围内。

核心部分代码

const int maxn = 10;

const double pi = 3.14159265358979323846, eps = 1e-6;
// 向量或点
struct cp {
    double x, y;
    cp(double a = 0, double b = 0) { x = a, y = b; }
};
// 向量运算
double operator * (cp a, cp b) { return a.x * b.x + a.y * b.y; } // 点积
double operator ^ (cp a, cp b) { return a.x * b.y - b.x * a.y; } // 叉积
double len(cp a) { return sqrt(a.x * a.x + a.y * a.y); }
cp operator * (cp a, double k) { return cp(a.x * k, a.y * k); }
cp operator * (double k, cp a) { return a * k; }
cp operator / (cp a, double k) { return a * (1.00 / k); }
cp rotate(cp a, double theta) { return cp(a.x * cos(theta) - sin(theta) * a.y, a.y * cos(theta) + sin(theta) * a.x); }
cp operator + (cp a, cp b) { return cp(a.x + b.x, a.y + b.y); }
cp operator - (cp a, cp b) { return cp(a.x - b.x, a.y - b.y); }
cp operator >> (cp a, cp b) { return b - a; } // 点a指向点b
cp operator << (cp a, cp b) { return a - b; }
bool operator == (cp a, cp b) { return (fabs(a.x - b.x) <= eps && fabs(a.y - b.y) <= eps); }
bool cmpx(cp a, cp b) {
    if(abs(a.x - b.x) > eps) return a.x < b.x;
    else return a.y < b.y;
} 

int n, m, ans = 0;
cp p1[maxn], p2[maxn];

int check(cp a) {
    if(a.x >= eps && a.x <= 100 - eps && a.y >= eps && a.y <= 100 - eps) return 1;
    else return 0;
}

cp cross(cp x, cp a, cp b) { // 利用一次函数求对称点
    if(fabs(a.y - b.y) <= eps) { return cp(x.x, 2 * a.y - x.y); }
    if(fabs(a.x - b.x) <= eps) { return cp(2 * a.x - x.x, x.y); } 
    double k1, k2, b1, b2, xx, yy;
    k1 = (a.y - b.y) / (a.x - b.x);
    b1 = a.y - k1 * a.x;
    k2 = -1.00 / k1;
    b2 = x.y - k2 * x.x;
    xx = (b2 - b1) / (k1 - k2);
    yy = k1 * xx + b1;
    return cp(2 * xx - x.x, 2 * yy - x.y);
} 

void dfs(cp a, int k) {
    if(k == 0) { ans += check(a); return ; }
    if(((p2[k] - p1[k]) ^ (a - p1[k])) <= eps) return ; // 在右边就return掉
    cp to = cross(a, p1[k], p2[k]);
    if(a == to) return;
    dfs(a, k - 1);
    dfs(to, k - 1);
}

void solve() {
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> p1[i].x >> p1[i].y >> p2[i].x >> p2[i].y;
    cin >> m;
    while(m--) {
        cp a;
        ans = 0;
        cin >> a.x >> a.y;
        dfs(a, n);
        cout << ans; el;
    }
}
posted @ 2026-02-02 10:42  ACehomoxue  阅读(12)  评论(1)    收藏  举报