传送门

B. Boundary

题意:

给 n 个点,问最多有多少个点可以和原点共圆, n 个点各不相同,且不与原点重合

思路:

(官方正解)O(n ^ 3) 肯定T飞,利用同弧所对的圆周角相等这一性质来做,首先枚举一个点 P,再枚举第二个点 A,求出所有的 ∠OAP,max( 其众数的个数 + 1 ) 就是答案(这里的1是第一个枚举的点P)。

需要注意的是,如果用 map 记录每个角的大小来求众数会被精度卡掉,可以存到double数组里再排个序,扫一遍即可,这里判相等的时候 eps 需要设到 1e-11 以下,否则会被卡。

还有一个问题,我还是截个题解的图8:

科普一下叉积的小知识:两个向量 P、Q,

P × Q > 0 说明 P 在 Q 的顺时针方向

P × Q < 0 说明 P 在 Q 的逆时针方向

P × Q = 0 说明 P 与 Q 共线,可能同向也可能反向

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double eps = 1e-11;    //1e-10 WA了 qaq
const int N = 2e3 + 5;
 
struct point
{
    double x, y;
    point() {};
    point(double xx, double yy):x(xx), y(yy) {}
    double operator *(const point &b)const    //点乘
    {
        return x * b.x + y * b.y;
    }
    double operator ^(const point &b)const    //叉乘
    {
        return x * b.y - y * b.x;
    }
} s[N];
 
double len(point a)    //向量的模
{
    return 1.0 * sqrt(a.x * a.x + a.y * a.y);
}
 
double cos(point P, point A)    //求 cos∠OAP
{
    point a = point(0.0 - A.x, 0.0 - A.y);
    point b = point(P.x - A.x, P.y - A.y);
    return a * b / (len(a) * len(b));
}
 
int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        for(int i = 1; i <= n; ++i)
        {
            scanf("%lf%lf", &s[i].x, &s[i].y);
        }
        if(n < 3)
        {
            cout<<n<<'\n';
            continue;
        }
        int ans = 0;
        for(int i = 1; i <= n; ++i)
        {
            vector<double>vec;
            for(int j = 1; j <= n; ++j)
            {
                if(j == i)
                    continue;
                if((s[i] ^ s[j]) < 0)
                    vec.push_back(cos(s[i], s[j]));
            }
            sort(vec.begin(), vec.end());
            int siz = vec.size();
            if(siz)
            {
                int cnt = 1;
                for(int j = 1; j < siz; ++j)
                {
                    if(fabs(vec[j] - vec[j - 1]) < eps)
                        cnt++;
                    else
                    {
                        ans = max(ans, cnt);
                        cnt = 1;
                    }
                }
                ans = max(ans, cnt);
            }
            vec.clear();
        }
        cout<<ans + 1<<'\n';
    }
    return 0;
}

C. Cover the Tree

题意:

给一棵 n 个点的树,问最少要用多少条树链,覆盖整棵树,只输出树链的起点和终点

思路:

话说这题不应该是最小链覆盖?

先求一个dfs序,然后把叶子节点两两配对,怎么配对呢?看题解:

缩一下句就是一共 s 个叶子节点,把第 i 个叶子节点和第 i + (s + 1) / 2 个叶子节点成对输出就好了

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 7;
 
vector<int>g[N];
vector<int>q;
int n;
 
void dfs(int s, int pre)
{
    if(g[s].size() == 1)
        q.push_back(s);
    int siz = g[s].size();
    for(int i = 0; i < siz; ++i)
    {
        if(g[s][i] != pre)
        {
            dfs(g[s][i], s);
        }
    }
}
 
int main()
{
    int u, v;
    while(~scanf("%d", &n))
    {
        q.clear();
        for(int i = 1; i <= n; ++i)
            g[i].clear();
        for(int i = 1; i < n; ++i)
        {
            scanf("%d%d", &u, &v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        if(n == 1)
        {
            cout<<0<<'\n';
            continue;
        }
        if(n == 2)
        {
            cout<<1<<'\n'<<"1 2"<<'\n';
            continue;
        }
        dfs(1, 0);
        int siz = q.size();
        int cnt = (siz + 1) / 2;
        cout<<cnt<<'\n';
        for(int i = 0; i < cnt; ++i)
        {
            cout<<q[i]<<' '<<q[(i + siz / 2) % siz]<<'\n';
        }
    }
    return 0;
}

D. Duration

 u1s1写完这道题我们就挂机了

#include<bits/stdc++.h>
using namespace std;
string s[3];
int main()
{
    cin >> s[0];
    cin >> s[1];
    sort(s, s + 2);
    int h = (s[1][0] - s[0][0]) * 10 + (s[1][1] - s[0][1]);
 
    int m = (s[1][3] - s[0][3]) * 10 + (s[1][4] - s[0][4]);
    int sec = (s[1][6] - s[0][6]) * 10 + (s[1][7] - s[0][7]);
 
    cout << h * 3600 + m * 60 + sec << endl;
}

F. Fake Maxpooling

 

题意:

n * m 的矩阵中 lcm[i][j] = lcm(i, j),求所有 k * k 的子矩阵中最大值的和

思路:

(滑动窗口)

横向纵向分别用一次单调队列:先用两层循环求出以 lcm[i][j] 为起点、长度为 k 的横向区间的最大值,存到res[i][j]中;再两层循环求出以res[i][j]为起点、长度为 k 的纵向区间的最大值(也就是以 lcm[i][j] 为左上角、大小为 k * k 的子矩阵中的最大值),累加起来即为答案。

如何用单调队列:

队首存最大值的下标,向后遍历到一位时,将队列中小于该值的元素从队首弹出,把该值放入队尾(由于对队首一直存的最大值,现在队里就这一个值了),然后把当前长度为 k 的区间之前的元素都弹出(当然是在队首了),操作完后队首的元素就是该区间内的最大值。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e3 + 5;
int lcm[N][N], res[N][N], cnt;
 
int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}
 
int main()
{
    int n, m, k;
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= m; ++j)
            lcm[i][j] = i / gcd(i, j) * j;
    deque<int>q;
    for(int i = 1; i <= n; ++i)
    {
        cnt = 0;
        q.clear();
        for(int j = 1; j <= k; ++j)
        {
            while(q.size() && lcm[i][q.back()] <= lcm[i][j])
                q.pop_back();
            q.push_back(j);
        }
        res[i][++cnt] = lcm[i][q.front()];
        for(int j = k + 1; j <= m; ++j)
        {
            while(q.size() && lcm[i][q.back()] <= lcm[i][j])
                q.pop_back();
            q.push_back(j);
            if(q.size() && q.front() < j - k + 1)
                q.pop_front();
            res[i][++cnt] = lcm[i][q.front()];
        }
    }
    ll ans = 0;
    for(int j = 1; j <= cnt; ++j)
    {
        q.clear();
        for(int i = 1; i <= k; ++i)
        {
            while(q.size() && res[q.back()][j] <= res[i][j])
                q.pop_back();
            q.push_back(i);
        }
        ans += 1ll * res[q.front()][j];
        for(int i = k + 1; i <= n; ++i)
        {
            while(q.size() && res[q.back()][j] <= res[i][j])
                q.pop_back();
            q.push_back(i);
            if(q.size() && q.front() < i - k + 1)
                q.pop_front();
            ans += 1ll * res[q.front()][j];
        }
    }
    cout<<ans<<'\n';
    return 0;
}

J. Just Shuffle 

 

 题意:

求一个置换P,使 A(1,2,3.....,n) 经过 K 次置换后变为排列 B 

思路:

这里求的 P 也就是 A 置换一次得到的结果。

对 A 的每一个循环节单独处理,设某位置 A 的循环节长度为 r ,假如 B 置换 Z 次后会回到 A,也就是 A 置换 K * Z 次后会再回到 A,这里 Z 满足 K * Z % r == 0 。那么要求 P 的话,也就是求一个 Z1,使 K * Z1 % r == 1。

参考:https://blog.csdn.net/qq_43552826/article/details/107353622?%3E

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double eps = 1e-11;
const int N = 1e5 + 5;
ll a[N], b[N], n, k;
bool vis[N];
vector<ll>vec;

void solve()
{
    ll r = vec.size(), inv;
    for(ll j = r - 1; j >= 0; --j)
    {
        if((k * j) % r == 1)
        {
            inv = j;
            break;
        }
    }
    for(ll j = 0; j < r; ++j)
        b[vec[j]] = vec[(j + inv) % r];
}

int main()
{
    while(~scanf("%lld%lld", &n, &k))
    {
        for(ll i = 1; i <= n; ++i)
        {
            scanf("%lld", &a[i]);
        }
        for(ll i = 1; i <= n; ++i)
        {
            if(vis[i]) continue;
            vec.clear();
            ll x = a[i];
            while(!vis[x])
            {
                vec.push_back(x);
                vis[x] = 1;
                x = a[x];
            }
            solve();
        }
        for(ll i = 1; i <= n; ++i)
        {
            cout<<b[i];
            if(i < n)
                cout<<' ';
        }
        cout<<'\n';
    }
    return 0;
}

 第二场先到这儿吧,去补第一场了