『模拟赛』CSP-S模拟12

Rank

有点烂

image

A. 小 h 的几何

虽然但是看起来这就是签。赛时看到计算几何直接润了,没看到送的 20pts。

主要问题在证一个结论:九点圆圆心位于垂心和外心的中点。几何证法见此,用到的全是初中知识,很好懂。证完就很水了,圆心即为 \(\frac{A+B+C}{2}\),随便算个选中的方案数再乘上总概率就完了。系数是 \(\frac{(n-1)(n-2)}{2}\times \frac{6}{n(n-1)(n-2)}=\frac{3}{n}\)。复杂度线性。

点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
	char ch = getchar();lx x = 0 , f = 1;
	for(;ch<'0'||ch>'9';ch = getchar()) if(ch == '-') f = -1;
	for(;ch>= '0' && ch<= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch^48);
	return x*f;
}
#undef lx
#define qr qr()
#define pii pair<int, int>
#define M_P(a, b) make_pair(a, b)
#define fi first
#define se second
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 5e5 + 5, M = 5e5;
const int mod = 998244353;
const double pai = 3.1415926535897932384626;
int n;
double ansx, ansy;
namespace Wisadel
{
    short main()
    {
        freopen("geometry.in", "r", stdin) , freopen("geometry.out", "w", stdout);
        n = qr;
        fo(i, 1, n)
        {
            double a; cin >> a;
            a = (a * pai) / (1.0 * 1e9);
            ansx += cos(a) * 3 / (2.0 * n);
            ansy += sin(a) * 3 / (2.0 * n);
        }
        printf("%.11lf %.11lf\n", ansx, ansy);
        return Ratio;
    }
}
signed main(){return Wisadel::main();}

B. 小 w 的代数

一直以为这道是签,特殊性质狂挂,只有 20pts,寄。

正解果然是树形 dp,用到了圆方树优化,圆点统计方点转移。设 \(f_{i,j}\) 表示到点 \(i\) 链尾为 \(j\) 的方案数。发现在链上是好转移的,考虑怎么在环上转移。我们可以拆环成链,在这道题上体现就是顺逆时针分别考虑,二者只会在进入的点的答案上重复统计,其余由于顺序不同方案都是不交的。然后就做完了,复杂度是 \(\mathcal{O(n^3)}\) 的,不是很优但足够通过本题。

然后就是由这道题发现的小知识。Tarjan 求点双时,判断形成点双的条件写 low[v] >= dfn[u] 是一定对的,而如果判断条件写了取等,则一定不能判掉由父亲过来的边,即不能写 if(v == fa) continue;。证明很好证,简单手模就出来了。

点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
	char ch = getchar();lx x = 0 , f = 1;
	for(;ch<'0'||ch>'9';ch = getchar()) if(ch == '-') f = -1;
	for(;ch>= '0' && ch<= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch^48);
	return x*f;
}
#undef lx
#define qr qr()
#define pii pair<int, int>
#define M_P(a, b) make_pair(a, b)
#define fi first
#define se second
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 2e5 + 5, M = 5e5;
const int mod = 998244353;
int n, m, tot;
int dfn[N], low[N], dt;
int hh[N], to[N << 1], ne[N << 1], cnt;
int f[1005][1005], g[N], s;
ll ans;
vector<int> e[N], zc;
stack<int> st;
namespace Wisadel
{
    void Wadd(int u, int v)
    {
        to[++cnt] = v;
        ne[cnt] = hh[u];
        hh[u] = cnt;
    }
    void Wtarjan(int u, int fa)
    {
        dfn[u] = low[u] = ++dt, st.push(u);
        for(int i = hh[u]; i != -1; i = ne[i])
        {
            int v = to[i];
            if(v == fa) continue;
            if(!dfn[v])
            {
                Wtarjan(v, u);
                low[u] = min(low[u], low[v]);
                if(low[v] >= dfn[u])
                {
                    tot++;
                    while(st.size())
                    {
                        int zc = st.top(); st.pop();
                        e[zc].P_B(tot), e[tot].P_B(zc);
                        if(zc == v) break;
                    }
                    e[u].P_B(tot), e[tot].P_B(u);
                }
            }
            else low[u] = min(low[u], dfn[v]);
        }
    }
    void Wdfs(int u, int fa)
    {
        if(u <= n)
        {
            fo(i, s, u - 1) ans = (ans + f[u][i]) % mod;
            fo(i, s, u - 1) f[u][u] = (f[u][u] + f[u][i]) % mod;
        }
        else
        {
            int cz = 0, len = e[u].size();
            fo(i, 0, len - 1) if(e[u][i] == fa){cz = i; break;}
            zc.clear();
            for(int i = (cz + 1) % len; i != cz; i = (i + 1) % len) zc.P_B(e[u][i]);
            fo(i, s, n) g[i] = f[fa][i];
            for(int v : zc)
            {
                fo(i, s, n) f[v][i] = (f[v][i] + g[i]) % mod;
                fo(i, s, v - 1) g[v] = (g[v] + g[i]) % mod;
            }
            fo(i, s, n) g[i] = f[fa][i];
            reverse(zc.begin(), zc.end());
            for(int v : zc)
            {
                fo(i, s, n) f[v][i] = (f[v][i] + g[i]) % mod;
                fo(i, s, v - 1) g[v] = (g[v] + g[i]) % mod;
            }
            for(int v : zc) fo(i, s, n)
                f[v][i] = (f[v][i] - f[fa][i] + mod) % mod;
        }
        for(int v : e[u]) if(v != fa) Wdfs(v, u);
    }
    short main()
    {
        freopen("algebra.in", "r", stdin) , freopen("algebra.out", "w", stdout);
        n = qr, m = qr;
        memset(hh, -1, sizeof hh);
        fo(i, 1, m)
        {
            int a = qr, b = qr;
            Wadd(a, b), Wadd(b, a);
        }
        tot = n;
        Wtarjan(1, 0);
        fo(i, 1, n)
        {
            fo(j, 1, n) fo(k, 1, n) f[j][k] = 0;
            f[i][i] = 1;
            s = i;
            Wdfs(i, i);
        }
        ans = (ans + n) % mod;
        printf("%lld\n", ans);
        return Ratio;
    }
}
signed main(){return Wisadel::main();}

C. 小 y 的数论

nt 题面完全看不懂。据 xrlong 所说这是一道集合了诸多板子的题。

D. 小 j 的组合

真正的签。

考虑每次选择点相当于给这个点多一次经过机会,理解了这一点就很好办了。考虑每一个点最后都会回到首尾这一条链上,那么首尾链最长时可得加点次数最少。这又是一棵树,因此问题就变成了求树的直径。范围很小,直接 Floyd 求就行。然后 dfs 模拟跑一遍即可。不用 Floyd 复杂度大概是线性的,用了 \(\mathcal{O(n^3)}\)\(n\le 100\),影响不大。

点击查看代码
#include<bits/stdc++.h>
#define fo(x, y, z) for(register int (x) = (y); (x) <= (z); (x)++)
#define fu(x, y, z) for(register int (x) = (y); (x) >= (z); (x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
	char ch = getchar();lx x = 0 , f = 1;
	for(;ch<'0'||ch>'9';ch = getchar()) if(ch == '-') f = -1;
	for(;ch>= '0' && ch<= '9';ch = getchar()) x = (x<<3) + (x<<1) + (ch^48);
	return x*f;
}
#undef lx
#define qr qr()
#define pii pair<int, int>
#define M_P(a, b) make_pair(a, b)
#define fi first
#define se second
#define P_B(x) push_back(x)
const int Ratio = 0;
const int N = 2e5 + 5, M = 5e5;
int n, tot;
int d[105][105];
int hh[N], to[N << 1], ne[N << 1], cnt;
bool yz[N];
vector<int> ans;
namespace Wisadel
{
    void Wadd(int u, int v)
    {
        to[++cnt] = v;
        ne[cnt] = hh[u];
        hh[u] = cnt;
    }
    void Wdfs(int u, int tar)
    {
        int zc = 0;
        yz[u] = 1; ans.P_B(u);
        for(int i = hh[u]; i != -1; i = ne[i])
        {
            int v = to[i];
            if(yz[v]) continue;
            if(d[tar][v] + 1 == d[tar][u])
            {
                zc = v;
                continue;
            }
            Wdfs(v, tar);
            printf("%d ", u);
            ans.P_B(++tot);
        }
        if(zc) Wdfs(zc, tar);
    }
    short main()
    {
        freopen("combo.in", "r", stdin) , freopen("combo.out", "w", stdout);
        n = qr;
        memset(hh, -1, sizeof hh);
        fo(i, 1, n) fo(j, i + 1, n) d[i][j] = d[j][i] = 1e9;
        fo(i, 1, n - 1)
        {
            int a = qr, b = qr;
            d[a][b] = d[b][a] = 1;
            Wadd(a, b), Wadd(b, a);
        }
        tot = n;
        fo(k, 1, n) fo(i, 1, n) fo(j, 1, n)
            d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
        int st = 1, ed = 1;
        fo(i, 1, n) fo(j, i + 1, n) if(d[i][j] > d[st][ed]) st = i, ed = j;
        printf("%d\n", n - d[st][ed] - 1);
        Wdfs(st, ed); puts("");
        for(int i : ans) printf("%d ", i);puts("");
        return Ratio;
    }
}
signed main(){return Wisadel::main();}

感觉一天打的好一天打得烂?然后中午和 CTH 浅算一下发现 CSP 和 NOIP 都是烂的那天?

CTH:你停一天不就行了。

感觉非联考的题好有水平啊,每次都有几个挺毒瘤的。

起码学了学圆方树,不知道九点圆等到回归 whk 后有没有机会用,不过学了总是好的。

明后天据说有 \(n\) 场比赛要打,模拟赛,ATCoder,CF,STAOI。。。到时候看改题情况决定打不打吧。


完结撒花!

艾雅法拉~

image

posted @ 2024-10-18 21:28  DrRatio  阅读(49)  评论(1编辑  收藏  举报