1. 程式人生 > 實用技巧 >牛客多校第二場補題

牛客多校第二場補題

D-Duration

簽到



F-Fake Maxpooling

先得求lcm矩陣

關於求\(lcm\)矩陣,一種方法是直接暴力列舉每一對\((i,j)\),然後暴力計算\(gcd(i,j),lcm(i,j)=i*j/gcd(i,j)\)

第二種是類似埃氏篩的思想,對於每一對互質的\((i,j)\),都去從1列舉一個k使得\(i*k<=n\) 並且 \(j*k<=m\),然後把每一對\((i*k,j*k)\)的gcd標記為k,這樣篩的效率就很高了(接近線性)

最後得到lcm矩陣的複雜度就是\(O(nm)\),剩下就是求每個k*k的正子矩陣的最大值了

由於是區間最大值,兩次滑動視窗即可(這個題stldeque沒被卡)

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define fastio ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
double pi = acos(-1);
const double eps = 1e-9;
const int inf = 1e9 + 7;
const int maxn = 5005;
 
int DP[maxn][maxn];
int A[maxn][maxn];
 
struct poi
{
    int id;
    ll num;
};
 
deque<poi>deq;
 
int __gcd(int a, int b)
{
    while (b)
    {
        ll tmp = b;
        b = a % b;
        a = tmp;
    }
    return 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++)
        {
            if (!DP[i][j])
                for (int u = 1; u * i <= n && u * j <= m; u++)
                    DP[u * i][u * j] = u, A[u * i][u * j] = i * j * u;
        }
 
    for (int i = 1; i <= n; i++)
    {
        deq.clear();
        int j = 1;
        for (int j = 1; j <= m; ++j)
        {
            int tmp = A[i][j];
            while (!deq.empty() && deq.front().id <= j - k)deq.pop_front();
            while (!deq.empty() && tmp > deq.back().num)
                deq.pop_back();
            deq.push_back({ j,tmp });
            DP[i][j] = deq.front().num;
        }
    }
    ll ans = 0;
    for (int j = k; j <= m; ++j)
    {
        deq.clear();
        for (int i = 1; i <= n; ++i)
        {
            int tmp = DP[i][j];
            while (!deq.empty() && deq.front().id <= i - k)deq.pop_front();
            while (!deq.empty() && tmp > deq.back().num)
                deq.pop_back();
            deq.push_back({ i,tmp });
            if (i >= k)
                ans += deq.front().num;
        }
    }
    printf("%lld\n", ans);
 
    return 0;
 
}


C-Cover the Tree

比賽的時候有思路,忘記寫了

先記錄入度為1的點計數為sum,ans就是點數/2上取整;然後從入度不為1的點開始dfs,把每個入度為1的點的dfs記錄下來,把i和sum / 2 + i連線起來就可以將所有的邊覆蓋(不太會證。。。畫圖舉例吧)

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#include<cstdio>
//#include<iostream>
//#include<vector>
//#include<cstring>
//#include<string>
//#include<algorithm>
#define ll long long
#define fastio {ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);};
using namespace std;
double pi = acos(-1);
const double eps = 1e-9;
const int inf = 1e9 + 7;
const int maxn = 1e6 + 10;
const ll mod = 998244353;

vector<int>G[maxn];
int sum = 0;
int n;
vector<int>xu;

void dfs(int from, int last)
{
	if (G[from].size() == 1)
	{
		xu.push_back(from);
		return;
	}
	for (int i = 0; i < G[from].size(); i++)
	{
		int to = G[from][i];
		if (to != last)
			dfs(to, from);
	}
}

int main()
{
	fastio;
	cin >> n;
	for (int i = 1; i < n; i++)
	{
		int a, b;
		cin >> a >> b;
		G[a].push_back(b);
		G[b].push_back(a);
	}

	for (int i = 1; i <= n; i++)
		if (G[i].size() == 1)
			sum++;
	cout << (sum + 1) / 2 << endl;
	for (int i = 1; i <= n; i++)
		if (G[i].size() != 1)
		{
			dfs(i, -1);
			break;
		}
	for (int i = 0; i < sum / 2; i++)
		cout << xu[i] << " " << xu[sum - i - 1] << endl;
	if (sum & 1)
		cout << xu[sum / 2 ] << " " << xu[0] << endl;
	return 0;
}


B-Boundary

計算幾何,列舉每一個點i,得到與圓心連線所對的弦對應的弧,利用同弧所對的圓周角相同,再去列舉另一個點j,將∠oji存下來,其中的眾數就是ans

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define ll long long
#define fastio ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
double pi = acos(-1);
const double eps = 1e-12;
const int inf = 1e9 + 7;
const int maxn = 2e5 + 10;

int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); }
    while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = getchar();
    return f * x;
}


inline int dcmp(double x)//cmp x with 0
{
    if (fabs(x) <= eps)return 0;
    return x < 0 ? -1 : 1;
}
inline int cmp(double x, double y)
{
    //x>y return 1
    //x<y reutrn -1
    //x==y return 0
    return dcmp(x - y);
}

double max(double x, double y)
{
    return dcmp(x - y) > 0 ? x : y;
}
double min(double x, double y)
{
    return dcmp(x - y) < 0 ? x : y;
}

struct Point {
    double x, y;
    Point() {}
    Point(double xx, double yy) { x = xx; y = yy; }
    Point operator -(Point s) { return Point(x - s.x, y - s.y); }
    Point operator +(Point s) { return Point(x + s.x, y + s.y); }
    double operator *(Point s) { return x * s.x + y * s.y; }
    double operator ^(Point s) { return x * s.y - y * s.x; }
}p[2010];

double len(Point a) { return sqrt(a * a); }
double dis(Point a, Point b) { return len(b - a); }//兩點之間的距離

double cross(Point a, Point b, Point c)//叉乘,a為公共點
{
    return (b - a) ^ (c - a);
}
double dot(Point a, Point b, Point c)//點乘 ,a為公共點
{
    return (b - a) * (c - a);
}

double angle(Point a, Point b, Point c)//求兩個向量的夾角(餘弦定理),a為公共點 
{
    return acos(dot(a, b, c) / dis(b, a) / dis(c, a));
}

bool Cmp(double x, double y)
{
    if (cmp(x, y) == -1)return 1;
    return 0;
}

vector<double>rad;

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        double x, y;
        scanf("%lf %lf", &x, &y);
        p[i] = { x,y };
    }
    if (n == 1)
    {
        printf("1\n");
        return 0;
    }
    Point O = { 0.0,0.0 };
    int ans = 1;
    for (int i = 1; i <= n; i++)
    {
        rad.clear();
        for (int j = 1; j <= n; j++)
        {
            if (cross(O, p[i], p[j]) < 0)
                rad.push_back(dot(p[j], O, p[i]) / (dis(p[j], p[i]) * dis(p[j], O)));
        }
        sort(rad.begin(), rad.end(), Cmp);
        int size = rad.size();
        for (int l = 0, r; l < size; l = r)
        {
            for (r = l; r < size && cmp(rad[l], rad[r]) == 0; r++);
            ans = max(ans, r - l + 1);
        }
    }
    printf("%d\n", ans);
    return 0;

}