2023杭电多校(5)

1001

直接求点到线段最小距离即可

开始还以为要有啥优化,但是hdu的机子能跑1e8诶(

#include <iostream>
#include <cmath>
#include <bits/stdc++.h>
using namespace std;
#define eps 1e-10
/********** 定义点 **********/
struct Point{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y) {}
};
/********** 定义向量 **********/
typedef Point Vector;
/********** 向量 + 向量 = 向量 **********/
Vector operator + (Vector a,Vector b)
{
    return Vector(a.x+b.x,a.y+b.y);
}
/********** 点 - 点 = 向量 **********/
Vector operator - (Point a,Point b)    
{
    return Vector(a.x-b.x,a.y-b.y);
}
/********** 向量 * 数 = 向量 **********/
Vector operator * (Vector a,double b)
{
    return Vector(a.x*b,a.y*b);
}
/********** 向量 / 数 = 向量 **********/
Vector operator / (Vector a,double b)
{
    return Vector(a.x/b,a.y/b);
}
bool operator < (const Point& a,const Point& b)
{
    return a.x<b.x || (a.x==b.x && a.y<b.y);
}
int dcmp(double x)    //减少精度问题
{
    if(fabs(x)<eps)
        return 0;
    else 
        return x<0?-1:1;
}
bool operator == (const Point &a,const Point &b)    //判断两点是否相等
{
    return dcmp(a.x-b.x)==0 && dcmp(a.y-b.y)==0;
}
/********** 向量点积 **********/
double Dot(Vector a,Vector b)
{
    return a.x*b.x+a.y*b.y;
}
/********** 向量长度 **********/
double Length(Vector A)
{
    return sqrt(Dot(A,A));
}
/********** 两向量角度 *********/
double Angle(Vector A,Vector B)
{
    return acos(Dot(A,B)/Length(A)/Length(B));
}

/********** 2向量求叉积 **********/
double Cross(Vector a,Vector b)
{
    return a.x*b.y-b.x*a.y;
}
/********** 3点求叉积 **********/
double Cross(Point a,Point b,Point c)
{
    return (c.x-a.x)*(b.y-a.y) - (c.y-a.y)*(b.x-a.x);
}
/********** 向量旋转 ***********/
Vector Rotate(Vector A,double rad)
{
    return Vector(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad));
}
/********** 向量的单位法线 *********/
Vector Normal(Vector A)
{
    double L = Length(A);
    return Vector(-A.y/L,A.x/L);
}
/********** 点和直线 **********/
/********** 求两点间距离 **********/
double dist(Point a,Point b)
{
    return sqrt( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) );
}
/********** 直线交点 **********/
Point GetLineIntersection(Point P,Vector v,Point Q,Vector w)
{
    Vector u = P-Q;
    double t = Cross(w,u) / Cross(v,w);
    return P+v*t;
}
/********** 点到直线的距离 ***********/
double DistanceToLine(Point P,Point A,Point B)
{
    Vector v1 = B-A,v2 = P-A;
    return fabs(Cross(v1,v2)) / Length(v1);    //如果不取绝对值,得到的是有向距离
}
/********** 点到线段的距离 **********/
double DistanceToSegment(Point P,Point A,Point B)
{
    if(A==B) return Length(P-A);
    Vector v1 = B-A,v2 = P-A,v3 = P-B;
    if(dcmp(Dot(v1,v2))<0) return Length(v2);
    else if(dcmp(Dot(v1,v3)) > 0) return Length(v3);
    else return fabs(Cross(v1,v2)) / Length(v1);
}
Point p1[10005],p2[10005];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); 
    cout.tie(0);
    int N,M;
    cin >> N >> M;
    for (int i = 1 ; i <= N ; i ++)
        cin >> p1[i].x >> p1[i].y;
    for (int i = 1 ; i <= M ; i ++)
        cin >> p2[i].x >> p2[i].y;
    for (int i = 1 ; i <= M ; i ++){
        double ans = 1e9+7;
        for (int j = 2 ; j <= N ; j ++){
            ans = min(ans,DistanceToSegment(p2[i],p1[j],p1[j-1]));
        }
        cout << fixed << setprecision(4) << ans << endl;
    }
    return 0;
}

1003

直接PAM+字符串hash遍历所有回文子串即可

显然,满足题意的子串一定回文,而且它切一半相等。后者哈希判断即可。

#include <bits/stdc++.h>
#define ull unsigned long long
#define ll long long
const ull h1 = 1145142017;
const int N = 1e5+10;
ull ha[N];
ull pw[N];
using namespace std;
ull GetHash(int l, int r) {
    if (l == 0) return ha[r];
    return ha[r] - ha[l - 1] * pw[r - l + 1];
}
bool Check(int L,int R){
    int Len = R-L+1;
    if (Len & 1) return false;
    int mid = (L+R)>>1;
    //cout << L << " " << R << " " << Len << " " << GetHash(L,mid)<< endl;
    return (GetHash(L,mid) == GetHash(mid+1,R)); 
}
ll Ans = 0;
struct Palindromic_Tree {
    int nxt[N][30], fail[N], cnt[N];
    int num[N], len[N], s[N], id[N];
    int last, n, p;
 
    int newnode(int l) {
        memset(nxt[p], 0, sizeof(nxt[p]));
        cnt[p] = num[p] = 0;
        len[p] = l;
        return p++;
    }
 
    void init() {
        p = 0;
        newnode(0), newnode(-1);
        last = n = 0;
        s[0] = -1;
        fail[0] = 1;
    }
 
    int get_fail(int x) {
        while (s[n - len[x] - 1] != s[n]) x = fail[x];
        return x;
    }
 
    void add(int c) {
        c -= 'a';
        s[++n] = c;
        int cur = get_fail(last);
        if (!nxt[cur][c]) {
            int now = newnode(len[cur] + 2);
            fail[now] = nxt[get_fail(fail[cur])][c];
            nxt[cur][c] = now;
            num[now] = num[fail[now]] + 1;
        }
        last = nxt[cur][c];
        cnt[last]++, id[last] = n;
    }
 
    ll Count() {
        for (int i = p - 1; i >= 0; i--) cnt[fail[i]] += cnt[i];
        for (int i = 2; i < p; i++) {
            if (Check(id[i] - len[i], id[i] - 1)) {
                Ans += cnt[i];
            }
        }
        return 0;
    }
} pam;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); 
    cout.tie(0);
    pw[0] = 1;
    for (int i = 1 ; i <= N ; i ++)
        pw[i] = pw[i-1] * h1;
    int T;
    cin >> T;
    while(T--){
        Ans = 0;
        string ss;
        cin >> ss;
        pam.init();
        int Len = ss.length();
        for (int i = 0 ; i < Len ; i ++)
            pam.add(ss[i]);
        ha[0] = ss[0];
        for (int i = 1; i < Len; i++) {
            ha[i] = ha[i - 1] * h1 + ss[i];
        }
        pam.Count();
        cout << Ans << endl;
    }
}

1005

考虑一个比较经典的问题:

把$n$分成$m$个数的和,然后每个数字最大不超过$k$

那这个东西如果不考虑后面说的,显然直接隔板

那考虑到后面说的就容斥一下就好啦。用$(-1)^k$那个容斥

然后$*n!$把所有的数字放进我们的求和里,再$*\frac{1}{m!}$处理一下组的顺序问题就好

实际上赛场上并没有非常想清楚$\frac{1}{m!}$,想着反正肯定$n!$得乘上去,然后对比了一下样例发现差个$m!$,交了一发发现过了(

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e6+10;
const int mod = 998244353;
int n,m,k,fac[maxn],inv[maxn];
int quick(int x,int n)
{
    int ans = 1;
    for( ; n ; n>>=1,x=x*x%mod )
        if( n&1 )    ans = ans*x%mod;
    return ans;
}
int C(int n,int m)
{
    if( m>n )    return 0;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
signed main()
{
    ios::sync_with_stdio( false ); cin.tie( 0 ); cout.tie( 0 );
    fac[0] = 1; inv[0] = 1;
    for(int i=1;i<=1000000;i++)
        fac[i] = fac[i-1]*i%mod, inv[i] = quick( fac[i],mod-2 );
    int t; cin >> t;
    while( t-- )
    {
        cin >> k >> m >> n;
        int ans = C(k-1,m-1);
        for(int i=1; ;i++)
        {
            if( k-1-n*i<0 )    break;
            if( i&1 )    ans = ( ans-C(m,i)*C(k-1-n*i,m-1)%mod +mod)%mod;
            else    ans = ( ans+C(m,i)*C(k-1-n*i,m-1)%mod )%mod;
        }
        ans = (1ll*ans * fac[k] %mod * inv[m]%mod)%mod;
        cout << ans << endl;
    }
}

1006

发现颜色数非常少,而且只和两个bag的颜色有关系

于是直接$f[i][j][k]$表示考虑到$i$,1袋颜色为$j$,2袋颜色为$k$的答案即可。

#include <bits/stdc++.h>
using namespace std;
int T;
int dp[100005][5][5];
string ss;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); 
    cout.tie(0);
    cin >> T;
    while (T--){
        cin >> ss;
        int Len = ss.length();
        for (int i = 0 ; i < Len ; i ++)
            for (int j = 0 ; j <= 3 ; j ++)
                for (int k = 0 ; k <= 3 ; k ++)
                    dp[i][j][k] = -1e9;
        for (int i = 0 ; i < Len ; i ++){
            int x;
            if (ss[i] == 'R') x = 1;
            else if (ss[i] == 'G') x = 2;
            else x = 3;
            dp[i][x][0] = max(dp[i][x][0],dp[i-1][0][0]);
            dp[i][0][0] = max(dp[i][0][0],dp[i-1][0][0]);
            if (i == 0) continue;
            for (int j = 1 ; j <= 3 ; j ++)
                for (int k = 0 ; k <= 3 ;k ++){
                    dp[i][j][k] = max(dp[i][j][k],dp[i-1][j][k]);
                    if (k == 0){
                        dp[i][j][x] = max(dp[i][j][x],dp[i-1][j][k]);
                        continue;
                    }
                    if (x == j && x == k){
                        for (int l = 1 ; l <= 3 ; l ++)
                            dp[i][l][0] = max(dp[i][l][0],dp[i-1][x][x] + 1);
                    }
                    if (x!=j && x!=k && j!=k){
                        for (int l = 1 ; l <= 3 ; l ++)
                            for (int m = 1 ; m <= 3 ; m ++){
                                dp[i][l][m] = max(dp[i][l][m],dp[i-1][j][k]);
                            }
                    }
                }
        }
        int ans = 0;
        for (int i = 0 ; i <= 3 ; i ++)
            for (int j = 0 ; j <=3 ; j ++)
                ans = max(ans,dp[Len-1][i][j]);
        cout << ans << endl;
    }
}

1007

队友写的签到,并不知道是啥(

但他预处理没做好被卡了3发,令人感慨(

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>

#define x first
#define y second
#define pb push_back

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 1000010;
const int p = 998244353;

int n, m, a, b;
int fact[N], infact[N];

int qmi(int a, int k)
{
    if (k == 0)
        return 1;
    if (k < 0)
        exit(0);
    int res = 1;
    while (k)
    {
        if (k & 1)
            res = (LL)res * a % p;
        a = 1ll * a * a % p;
        k >>= 1;
    }

    return res;
}

void init()
{
    fact[0] = 1, infact[0] = 1;
    for (int i = 1; i < N; i++)
    {
        fact[i] = (LL)fact[i - 1] * i % p;
        infact[i] = (LL)infact[i - 1] * qmi(i, p - 2) % p;
    }
}
int pw[N+10],pw1[N+10];
void solve()
{
    cin >> n >> m >> a >> b;
    int ans = 0;
    int sum = 0;
    int w0 = qmi(b, p - 2);
    int w1 = (LL)a * w0 % p;
    int w2 = (LL)(b - a) * w0 % p;
    pw[0] = pw1[0] = 1;
    for (int i = 1 ; i <= n ; i ++){
        pw[i] = 1ll * pw[i-1] * w1%p; 
        pw1[i] = 1ll * pw1[i-1] * w2 %p;
        
    }
    for (int i = 1; i <= n; i++)
    {
        LL res = 1;
        res = (LL)res * fact[n] % p * infact[i] % p * infact[n - i] % p;
        res = (LL)res * pw[i] % p;
        res = (LL)res * pw1[n-i] % p;
        sum = (LL)(sum + qmi(i, m)) % p;
        ans = (LL)(ans + res * sum % p) % p;
    }
    cout << (ans % p + p) % p << endl;
    return;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int T = 1;
    cin >> T;

    init();

    while (T--)
    {
        solve();
    }

    return 0;
}

1009

发现$dfs$两次即可,模拟一下这个过程

给队友讲了一遍思路扔给他写的(

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <cmath>

#define x first
#define y second
#define pb push_back

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 2000010, M = 4000010;

int n, m;
int h[N], h1[N], e[M], ne[M], idx;
int w[N];
vector<int> v[N];
int cnt;
int from[N];
int d[N];
int ans;

void add(int h[], int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int u, int fa, int num)
{
    cnt = max(cnt, num);
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (j == fa)
            continue;
        if (w[u] == j)
        {
            v[num].pb(j);
            dfs(j, u, num);
        }
        else
        {
            // cout << "t2" << endl;
            ++cnt;
            v[cnt].pb(j);
            from[cnt] = num;
            dfs(j, u, cnt);
        }
    }
}

void dfs2(int u, int fa, int depth)
{
    ans = max(ans, depth);
    for (int i = h1[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (j == fa)
            continue;
        dfs2(j, u, depth + d[j]);
    }
}

void solve()
{
    cin >> n;
    memset(h, -1, sizeof h);
    memset(h1, -1, sizeof h1);
    memset(e, 0, sizeof e);
    memset(ne, 0, sizeof ne);
    memset(w, 0, sizeof w);
    memset(from, 0, sizeof from);
    memset(d, 0, sizeof d);
    for (int i = 1; i <= cnt; i++)
        v[i].clear();
    idx = cnt = ans = 0;

    for (int i = 1; i <= n; i++)
    {
        int t;
        cin >> t;
        if (t == 0)
            continue;

        add(h, i, t), add(h, t, i);
    }

    for (int i = 1; i <= n; i++)
        cin >> w[i];

    v[1].pb(1);
    dfs(1, -1, 1);

    d[1] = ceil(log(2 * v[1].size()) / log(2));
    for (int i = 1; i <= cnt; i++)
    {
        d[i] = d[from[i]] + ceil(log(2 * v[i].size()) / log(2));
    }

    int ans = 0;
    for (int i = 1; i <= cnt; i++)
        ans = max(ans, d[i]);

    // for (int i = 1; i <= cnt; i++)
    //     cout << i << " from " << from[i] << endl;

    // for (int i = 2; i <= cnt; i++)
    // {
    //     add(h1, i, from[i]), add(h1, from[i], i);
    // }
    // dfs2(1, -1, d[1]);

    cout << ans << endl;
    // cout << cnt << endl;
    return;
}

int main()
{
    int size(512 << 20);                                            // 512M
    __asm__("movq %0, %%rsp\n" ::"r"((char *)malloc(size) + size)); // YOUR CODE

    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    int T = 1;
    cin >> T;

    while (T--)
    {
        solve();
    }

    exit(0);
}

1012

暴力算一下度然后算个组合数即可

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MOD = 1000000007;
const int mx = 1e6; 
int d[mx+10];
int ans[mx+10];
int fac[mx+10],inv[mx+10];
int C(int n,int r){
    return fac[n] * inv[r] % MOD * inv[n-r]%MOD;
}
int Pow(int x,int y){
    int ans = 1;
    for (;y;y>>=1){
        if (y & 1) ans = 1ll *ans * x %MOD;
        x = 1ll * x * x %MOD;
    }
    return ans;
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0); 
    cout.tie(0);
    fac[0] = 1;
    int T;
    cin >> T;
    for (int i = 1 ; i <= mx ; i ++)
        fac[i] = 1ll * fac[i-1] * i %MOD;
    inv[mx] = Pow(fac[mx],MOD-2);
    for (int i = mx-1 ; i >= 1 ; i --)
        inv[i] = 1ll*inv[i+1]*(i+1)%MOD; 
    inv[0] = 1;
    //cout << C(2,2) << endl; 
    while (T--){
        int N,M;
        cin >> N >> M;
        for (int i = 1 ; i <= N ;  i++)
            d[i] = ans[i] = 0; 
        for (int i =1 ; i <= M ; i ++){
            int u,v;
            cin >> u >> v;
            d[u] ++;
            d[v] ++; 
        }
        for (int i = 1 ; i <= N ; i ++){
            for (int j = 2 ; j <= d[i] ; j ++){
               (ans[j] += C(d[i],j))%=MOD;
            }
        }
        int ans1 = 0;
        for (int i = 1 ; i <= N ; i ++)
            ans1 ^= ans[i];
        cout << ans1 << endl;
    }
    return 0;
}

队友不在,接近单挑,好累哦(

1002想了一下,把那个$gcd(2^i-1,2^j-1)$=$2^{gcd(i,j)}-1$的结论搞了出来

然后发现是个非常经典的莫比乌斯反演+积性函数前缀和题,但是来不及写了(x

posted @ 2023-08-01 22:35  si_nian  阅读(156)  评论(0编辑  收藏  举报