Processing math: 100%

2023杭电多校第一场

手还是有点生(

随便写点东西.jpg

1001

实际上把链找出来之后就可以把树扔了(

然后在某个位置上出现的点它的出现位置就可以表示为kdis+bb是位置

对于S的每个数字,找它在T的位置,能拿到两个同余方程,形如x=t1(modn)x=t2(modm)找excrt最小正整数解即可。

代码是学弟写的,摸了(

复制代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#define int long long
using namespace std;
typedef long long LL;
const int N = 3010, M = 6010;

int n, m;
int h[N], e[M], ne[M], idx;
int depth[N], fa[N];
vector<int> va, vb;

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

void dfs(int Now ,int Fa){
    fa[Now] = Fa;
    for (int i = h[Now]; i != -1 ; i = ne[i]){
        int v = e[i];
        if (v == Fa) continue;
        depth[v] = depth[Now] + 1;
        dfs(v,Now);
    }
}
vector<int> lca(int st, int ed)
{
    bool flag = true;
    
    vector<int> vst, ved, res;
    
    if(depth[st] <= depth[ed]) 
    {
        swap(st, ed);
        flag = false;
    }
    
    while(depth[st] > depth[ed])
    {
        vst.push_back(st);
        st = fa[st];
    }
    
    while(st != ed)
    {
        vst.push_back(st);
        ved.push_back(ed);
        st = fa[st];
        ed = fa[ed];
    }
    
    vst.push_back(st);
    
    
    while(ved.size())
    {
        vst.push_back(ved.back());
        ved.pop_back();
    }

    if(!flag)
    {
        while(vst.size())
        {
            res.push_back(vst.back());
            vst.pop_back();
        }
        return res;
    } 
    
    return vst;
}

LL exgcd(LL a, LL b, LL &x, LL &y)
{
    if(b == 0)
    {
        x = 1, y = 0;
        return a;
    }

    LL d = exgcd(b, a % b, y, x);
    y -= a / b * x;

    return d;
}

signed main()
{
    int T;
    scanf("%lld", &T);
    
    while(T --)
    {
        scanf("%lld%lld", &n, &m);
        memset(h, -1, sizeof h);
        memset(e, 0, sizeof e);
        memset(ne, 0, sizeof ne);
        
        idx = 0;
        
        for(int i = 1; i <= n - 1; i ++)
        {
            depth[i] = 0;
            int a, b;
            scanf("%lld%lld", &a, &b);
            add(a, b), add(b, a);
        }    
        
        depth[1] = 1;
        dfs(1,1);
        
        while(m --)
        {
            LL ans = 1e9;
            int sa, ta, sb, tb;
            scanf("%lld%lld%lld%lld", &sa, &ta, &sb, &tb);
            va.clear(), vb.clear();
            
            vector<int> vaa = lca(sa, ta);
            vector<int> vbb = lca(sb, tb);
            
            int la = vaa.size();
            int lb = vbb.size();
            
            for(int i = 1; i < la; i ++) va.push_back(vaa[i]);
            for(int i = 1; i < lb; i ++) vb.push_back(vbb[i]);
            for(int i = la - 2; i >= 1; i --) va.push_back(vaa[i]);
            for(int i = lb - 2; i >= 1; i --) vb.push_back(vbb[i]);
            va.push_back(vaa[0]);
            vb.push_back(vbb[0]);
            la = va.size();
            lb = vb.size();
            int ans1 = 0;
            for(int i = 0; i < va.size(); i ++)
            {
                int numa = va[i];
                for(int j = 0; j < vb.size(); j ++)
                    if(va[i] == vb[j])
                    {
                        LL numb = vb[j];
                        LL x = 0;
                        LL a1 = la, a2 = lb;
                        LL m1 = (i + 1)%la, m2 = (j + 1)%lb;
                        LL k1, k2;
                        LL d = exgcd(a1, a2, k1, k2);
                        if((m2 - m1) % d != 0)
                        {
                            continue;
                        }
                        k1 *= (m2 - m1) / d; // 获得特解
                
                        LL t = a2 / d;
                        k1 = (k1 % t + t) % t; // 在通解中找到最小的解
                
                        x = k1 * a1 + m1; // 目前两个方程合并完后的x的特解
                
                        LL a = a1 / d * a2; // a1 a2的最小公倍数
                        m1 = k1 * a1 + m1;
                        a1 = a;

                    
                        if(x != -1) x = (m1 % a1 + a1) % a1; // 最小正整数解
                        if (x!= -1 && x < ans){
                            ans = x;
                            ans1 = numa;
                        }
                    }
            }
            if (ans != 1e9) printf("%lld\n", ans1);
            else printf("-1\n");
        }
    }    
}
复制代码

 

1002

f[i][0/1/2]表示当前点不选且被覆盖/选了/不选且不被覆盖

转移挺好写的

复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int T;
const int inf = 1e9+7;
const int mx = 2e5+5;
int a[mx];
vector<int> G[mx];
int dp[mx][3];
void dfs(int Now,int fa){
    dp[Now][1] = a[Now];
    dp[Now][0] = dp[Now][2] = 0;
    bool flag = false;
    int chg = inf;
    for (auto v : G[Now]){
        if (v == fa) continue;
        dfs(v,Now);
        dp[Now][1] += min(dp[v][0],min(dp[v][1],dp[v][2]));
        dp[Now][2] += min(dp[v][1],dp[v][0]);
        if (dp[v][1]  > dp[v][0]){
            dp[Now][0] += dp[v][0];
            chg = min(chg,dp[v][1]-dp[v][0]);    
        }else{
            flag = true;
            dp[Now][0] += dp[v][1];
        }
    }
    if (!flag)
        dp[Now][0] += chg;
}
signed main(){
    scanf("%lld",&T);
    while (T--){
        int N;
        scanf("%lld",&N);
        for (int i = 1 ; i <= N ; i ++){
            scanf("%lld",&a[i]);
            G[i].clear();
            dp[i][0] = dp[i][1] = dp[i][2] = 0;
        }
        for (int i = 1 ; i < N ; i ++){
            int x,y;
            scanf("%lld%lld",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x); 
        }
        dfs(1,1);
        printf("%lld\n",min(dp[1][1],dp[1][0]));
    }
    return 0;
}
复制代码

1005

把串二倍延长一下,实际上枚举M个位置就可以把所有的可能串给枚举上

字符串哈希一下,存一下最小的hash值,最后直接比较即可。

*998244353被卡了,差评

复制代码
#include<bits/stdc++.h>
using namespace std;
const int mx = 2e5+10;
const unsigned long long P = 429496729;
int N,M;
int T;
unsigned long long tt[mx]; 
unsigned long long p[mx];
unsigned long long pref[mx];
unsigned long long GetHash(int l, int r) {
    return pref[r] - pref[l - 1] * p[r - l + 1];
}
unsigned long long Hash[mx];
int main(){
    p[0] = 1;
    for (int i = 1 ; i <= mx - 10 ; i ++)
        p[i] = p[i-1] * P;
    cin >> T;
    while (T--){
        scanf("%d%d",&N,&M);
        for (int i = 1 ; i <= N ; i ++){
            Hash[i] = 0;
        }
        for (int i = 1 ; i <= N ; i ++){
            string ss;
            cin >> ss;
            ss = ss + ss;
            ss = ' ' + ss;
            int Len = ss.length();
            pref[0] = 0;
            for (int j = 1 ; j <= Len ; j ++){
                pref[j] = pref[j-1] * P + ss[j];
            }
            Hash[i] = pref[M];
            unsigned long long mn = Hash[i];
            for (int j = 1 ; j <= M ; j ++){
                unsigned long long x = GetHash(j,j+M-1); 
                mn = min(mn,x);
            }
            tt[i] = mn;
        }
        int Q;
        cin >> Q;
        while (Q--){
            int x,y;
            scanf("%d%d",&x,&y);
            if (tt[x] == tt[y]) printf("Yes");
            else printf("No");
            if (T!=0 || Q != 0) printf("\n"); 
        }
    }
} 
复制代码

1008

签到,跳过.jpg

1012

根据树删边博弈,SG[x] = (SG[v]+1)的xor和

vx的所有儿子

SG[root]如果是0先手赢

然后暴力换个根就好了,看有多少点的SG不是0

复制代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int cnt = 0;
int read(){
    int f=1,k=0;
    char c=getchar();
    while(c<'0'||c>'9'){
        c=getchar(); 
    }
    while(c>='0'&&c<='9'){
        k=k*10+c-'0';
        c=getchar();
    }
    return f*k;
}
const int mx = 2e5+10;
const int MOD =1e9+7;
vector<int> G[mx]; 
int T;
int N;
ll SG[mx];
int Fa[mx];
ll Pow(int x,int y){
    ll ans = 1;
    for (;y;y>>=1){
        if (y&1) ans = (ans * 1ll * x) %MOD;
        x = 1ll*x * x %MOD;
    }
    return ans;
}
void dfs(int Now,int fa){
    Fa[Now] = fa;
    SG[Now] = 0;
    for (auto v:G[Now]){
        if (v == fa) continue;
        dfs(v,Now);
        SG[Now] ^= (SG[v]+1);
    }
}
void dfs1(int Now,int fa){
    if (Now != 1){
        SG[fa] ^= (SG[Now]+1);
        SG[Now] = SG[Now] ^ (SG[fa] + 1);
    }
    if (SG[Now] != 0) cnt ++;
    for (auto v : G[Now]){
        if (v == fa) continue;
        dfs1(v,Now);
    }
    if (Now != 1){
        SG[Now] ^= (SG[fa]+1);
        SG[fa]^=(SG[Now] + 1);
    }
}
signed main(){
    T=read();
    while (T--){
        N = read(); 
        for (int i = 1 ; i <= N ; i ++){
            Fa[i] = 0;
            SG[i] = 0;
            G[i].clear();
        }
        for (int i = 1 ; i < N ; i ++){
            int x,y;
            x=read(),y=read();
            G[x].push_back(y);
            G[y].push_back(x);
        }
        dfs(1,1);
        cnt = 0;
        dfs1(1,1);
        ll inv = Pow(N,MOD-2);
        printf("%lld\n",1ll*inv*cnt%MOD);
    }
    return 0;
}
复制代码

 1003

简单的区间dp,可惜赛中没看,在写1010(

根据套路可以考虑f[l][r]表示lr全消干净的最大贡献

但是这些信息不够转移

所以多补两维

f[l][r][k][l]表示l,r之间剩一张k类型,等级为m的牌

f[l][r][0][0]表示消完

那么转移显然就是

f[l][r][k][m]=max(f[l][mid][k][m1],f[mid+1][r][k][l1],f[l][mid][k][m]+f[mid+1][r][0][0])

f[l][r][0][0]=max(f[l][mid][0][0]+f[mid+1][r][0][0],f[l][r][k][m]+val(k,m))

for一下结束(

复制代码
#include <bits/stdc++.h>
#define ll long long
const int mx = 105; 
using namespace std;
ll a[mx],b[mx],v[mx];
ll Level[11];
ll f[mx][mx][22][11];
const ll INF = 0x3f3f3f3f3f3f3f3f;
int main(){
    int T;
    cin >> T;
    while (T--){
        int N,M,lim,p;
        cin >> N >> M >> lim >> p;
        lim = min(7,lim);
        Level[1] = 1; 
        for (int i = 2 ; i <= lim ; i ++)
            Level[i] = Level[i-1] * p;
        for(int i = 1 ; i <= N ; i ++)
            cin >> a[i];
        for (int i = 1 ; i <= M ; i ++)
            cin >> v[i];
        memset(f,-0x3f,sizeof(f));
        for (int i = 1 ; i <= N ; i ++){
            f[i][i][0][0] = v[a[i]];
            f[i][i][a[i]][1] = 0;
        }
        for (int l = N ; l >= 1 ; l --)
            for (int r = l + 1 ; r <= N ; r++)
                for(int mid = l ; mid < r ; mid ++){
                    f[l][r][0][0] = max(f[l][r][0][0],f[l][mid][0][0]+f[mid+1][r][0][0]);
                    for (int j = 1 ; j <= M ; j ++){
                        f[l][r][j][1] = max(f[l][r][j][1],f[l][mid][j][1]+f[mid+1][r][0][0]); 
                        for (int k = 2 ; k <= lim ;k ++){
                            f[l][r][j][k] = max(f[l][r][j][k],f[l][mid][j][k-1]+f[mid+1][r][j][k-1]);
                            f[l][r][0][0] = max(f[l][r][0][0],f[l][r][j][k] +v[j]*Level[k]);
                        }
                    }
                }
        cout << f[1][N][0][0] << '\n';
    }
    return 0;
} 
复制代码

 

posted @   si_nian  阅读(107)  评论(3编辑  收藏  举报
欢迎阅读『2023杭电多校第一场』
点击右上角即可分享
微信分享提示