2022NOIPA层联测29

快乐一下:为了kuangbiaopilihu的形象我把它删掉了。

今天的T3的“部分分”不一定是假做法,比如我写的其它部分是正解好不容易记住了求前缀和,但是求了一个任意两点间最短路?!,而且特判了一个m<=10的小数据枚举排列打算保底结果保没了10pts,60->50

还有今天的T2,能想起来拓展欧拉定理(赛后才知道把指数和欧拉函数取模还有这么个高级的名字),也能想起来欧拉函数的求法,但是面对这么大的数据不知道怎么读入?!,于是就写了个10分的走人了……

我是个什么der……

 

A. A

状压dp,状态是这个后缀和有没有出现过,只记录有可能做贡献的后缀和,太大的都扔掉,出现过对应的二进制位就是1。

为了去重,第一次出现合法后缀后不再转移,加入一个新的数字就代表之前出现过的从0开始的所有后缀和都变大了,把“0位置”初始化成1可以在左移的时候直接添上新数的贡献。整体左移一位的写法看起来很美观好像对时间影响也不大就不改了。

max({p,q,r})这种写法头一次见真的可以过!?所以这东西NOIP能用吗,我不知道……

code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 2;
const int mod = 998244353;

int f[55][1<<20], n, m, p, q, r, x, all, ans;

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

inline ll qpow(ll a, ll b)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}
void add(int &x, int y) {x+=y; if(x>=mod) x-=mod;}

int main()
{
    freopen("a.in", "r", stdin);
    freopen("a.out", "w", stdout);
    
    n = read(), m = read();
    p = read(), q = read(), r = read();
    x = p + q + r; all = (1<<x+1)-1;
    f[0][1] = 1;
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<=all; j++)
        {
            if(!f[i][j]) continue;
            if((j&(1<<r))&&(j&(1<<q+r))&&(j&(1<<p+q+r))) continue;
            for(int k=1; k<=min(m,max({p,q,r})); k++) add(f[i+1][((j<<k)&all)^1], f[i][j]);
            if(m > max({p,q,r})) add(f[i+1][1], 1ll*f[i][j]*(m-max({p,q,r}))%mod);
        }
    }
    for(int i=1; i<=n; i++) for(int j=0; j<=all; j++)
        if((j&(1<<r))&&(j&(1<<q+r))&&(j&(1<<p+q+r))) add(ans, 1ll*f[i][j]*qpow(m,n-i)%mod);
    printf("%d\n", ans);

    return 0;
}

 

B. B

找个美观的拓展欧拉定理放一下qwq,(b-1)*bn-1

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e6 + 7;

ll mod, ans;
ll n, b, br, ur, e1, e2, sq;
char ns[maxn], bs[maxn];

inline ll read()
{
    ll x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

inline ll qpow(ll a, ll b)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

ll phi(ll n)
{
    ll ans = n, sq = sqrt(n);
    for(ll i=2; i<=sq; i++)
    {
        if(n % i == 0)
        {
            ans = ans / i * (i-1);
            while(n % i == 0) n /= i;
        }
    }
    if(n > 1) ans = ans / n * (n-1);
    return ans;
}

int main()
{
    freopen("b.in", "r", stdin);
    freopen("b.out", "w", stdout);
    
    scanf("%s%s", ns+1, bs+1); mod = read();//啊这,我读入读反了??
    e1 = strlen(bs+1), e2 = strlen(ns+1);
    for(int i=1; i<=e1; i++)
    {
        b = (b * 10 % mod + bs[i] - '0') % mod;
    }
    ur = phi(mod);
    bool flag = 0;
    for(int i=1; i<=e2; i++)
    {
        n = (n * 10 + ns[i] - '0');
        if(n > mod) flag = 1;
        if(flag) n %= ur;
    }
    if(flag) n = (n - 1) % ur + ur;
    else n--;
    ans = 1;
    ans = (b - 1 + mod) % mod;
    ll res3 = qpow(b, n);
    ans = ans * res3 % mod;
    printf("%lld\n", ans);

    return 0;
}

 

C. C

关于我为什么写了这么多dij而不直接把参数传进去,我也不知道当时怎么想的……一个确定了3个起点还去跑任意两点间最短路,拿T2的代码试T1的样例调不出来还以为是自己求欧拉函数求锅了的大Sily Cat。

CR:你是以什么精神状态在做题啊??

Cat:……

code
 #include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2e5 + 2;
const int mod = 998244353;
const ll inf = 0x3f3f3f3f3f3f3f3f;

int n, m, p1, p2, p3;
ll d1[maxn], d2[maxn], d3[maxn], ans=inf, w[maxn];
bool vis[maxn];
typedef pair<ll, int> pii;
priority_queue<pii, vector<pii>, greater<pii> > q;

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

struct node 
{
    int nxt, to, w;
}a[maxn<<1];
int head[maxn], len;

void add(int x, int y, int w)
{
    a[++len].to = y; a[len].nxt = head[x]; a[len].w = w;
    head[x] = len;
}

struct node2 
{
    int u, v;
}e[maxn];

void dij1(int s)
{
    for(int i=1; i<=n; i++) d1[i] = inf;
    for(int i=1; i<=n; i++) vis[i] = 0;
    d1[s] = 0;
    q.push(pii(d1[s], s));
    while(!q.empty())
    {
        int x = q.top().second; q.pop();
        if(vis[x]) continue;
        vis[x] = 1;
        for(int i=head[x]; i; i=a[i].nxt)
        {
            int y = a[i].to;
            if(d1[y] > d1[x] + a[i].w)
            {
                d1[y] = d1[x] + a[i].w;
                q.push(pii(d1[y], y));
            }
        }
    }
}

void dij2(int s)
{
    for(int i=1; i<=n; i++) d2[i] = inf;
    for(int i=1; i<=n; i++) vis[i] = 0;
    d2[s] = 0;
    q.push(pii(d2[s], s));
    while(!q.empty())
    {
        int x = q.top().second; q.pop();
        if(vis[x]) continue;
        vis[x] = 1;
        for(int i=head[x]; i; i=a[i].nxt)
        {
            int y = a[i].to;
            if(d2[y] > d2[x] + a[i].w)
            {
                d2[y] = d2[x] + a[i].w;
                q.push(pii(d2[y], y));
            }
        }
    }
}

void dij3(int s)
{
    for(int i=1; i<=n; i++) d3[i] = inf;
    for(int i=1; i<=n; i++) vis[i] = 0;
    d3[s] = 0;
    q.push(pii(d3[s], s));
    while(!q.empty())
    {
        int x = q.top().second; q.pop();
        if(vis[x]) continue;
        vis[x] = 1;
        for(int i=head[x]; i; i=a[i].nxt)
        {
            int y = a[i].to;
            if(d3[y] > d3[x] + a[i].w)
            {
                d3[y] = d3[x] + a[i].w;
                q.push(pii(d3[y], y));
            }
        }
    }
}

int fa[maxn], dep[maxn], siz[maxn], son[maxn], top[maxn];
void find_heavy_edge(int u, int fat, int depth)
{
    fa[u] = fat;
    dep[u] = depth;
    siz[u] = 1;
    son[u] = 0;
    int maxsize = 0;
    for(int i=head[u]; i; i=a[i].nxt)
    {
        int v = a[i].to;
        if(v == fat) continue;
        find_heavy_edge(v, u, depth+1);
        siz[u] += siz[v];
        if(siz[v] > maxsize)
        {
            maxsize = siz[v];
            son[u] = v;
        }
    }
}

void connect_heavy_edge(int u, int ancestor)
{
    top[u] = ancestor;
    if(son[u])
    {
        connect_heavy_edge(son[u], ancestor);
    }
    for(int i=head[u]; i; i=a[i].nxt)
    {
        int v = a[i].to;
        if(v == son[u] || v == fa[u]) continue;
        connect_heavy_edge(v, v);
    }
}

int LCA(int x, int y)
{
    while(top[x] != top[y])
    {
        if(dep[top[x]] < dep[top[y]]) swap(x, y);
        x = fa[top[x]];
    }
    if(dep[x] > dep[y]) swap(x, y);
    return x;
}

void solve2()
{
    find_heavy_edge(p1, p1, 1);
    connect_heavy_edge(p1, p1);
    int cnt1 = dep[p3] - 1, lca = LCA(p2, p3);
    int cnt2 = dep[p2] - dep[lca];
    sort(w+1, w+1+m);
    for(int i=2; i<=m; i++) w[i] += w[i-1];
    ans = w[cnt2] + w[cnt2+cnt1];
    printf("%lld\n", ans);
    exit(0);
}

int main()
{
    freopen("c.in", "r", stdin);
    freopen("c.out", "w", stdout);
    
    n = read(); m = read(); 
    p1 = read(), p2 = read(), p3 = read();
    for(int i=1; i<=m; i++)
    {
        int u = read(), v = read(); w[i] = read();
        add(u, v, 1); add(v, u, 1);
    }
    if(m == n - 1) solve2();
    sort(w+1, w+1+m);
    for(int i=2; i<=m; i++) w[i] += w[i-1];
    dij1(p1), dij2(p2), dij3(p3);
    for(int i=1; i<=n; i++)
    {
        if(d1[i] + d2[i] + d3[i] > m) continue;
        ll res = w[d2[i]] + w[d2[i]+d1[i]+d3[i]];
        ans = min(ans, res);
    }
    printf("%lld\n", ans);

    return 0;
}

 

D. D

我才知道原来任意的图都可以一笔覆盖偶数次,把一条边看成两条那么所有点的度数都是偶数了。于是偶数的情况就是连通块个数-1,奇数的话可以画偶数次回到原点这条线不断,等于直接考虑m=1的情况。

如果经过回路所有点度数-2,如果经过的不是回路端点度数-1其它点度数-2,奇点个数减少两个。

APJ:

假如我们有 n 个奇点,那么至少需要经过 n / 2 条路径才能使所有的奇点全变为偶点,所有点都变成偶点后就可以直接一次欧拉回路削完所有边了。

考虑如果有两条路径重合,我们可以将两条路径的一个端点交换,这样就可以使两条路径不相交,这样做若干次后一定可以使每两条路径不相交,那么就可以削完了。

关于/2是不是会向下取整,其实奇点个数不可能有奇数个

建图的话把覆盖的都合并,交叉的都建出新点,感觉mod mod mod Chen_jr的实现方式是最简洁的!!%%%%%%%%%%

code
 //鹤了
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2005;
const int mod = 998244353;

//这个神奇的数组大小我很不能理解
int n, m, tot, deg[maxn*maxn*10], val[maxn*maxn*10], ans, f[maxn*maxn*10];
bool del[maxn];
typedef pair<int, int> pii;
map<pii, int> id;

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

struct Line 
{
    int x1, x2, y;
    bool operator < (const Line &T) const 
    {
        return y < T.y;
    }
}h[maxn], z[maxn];
int ch, cz;
bool cover(Line L, Line R) {return L.y == R.y && ((L.x1>=R.x1&&L.x2<=R.x2)||(L.x2>=R.x1&&L.x2<=R.x2));}
bool check(Line H, Line Z) {return H.x1 <= Z.y && H.x2 >= Z.y && Z.x1 <= H.y && Z.x2 >= H.y;}
void uniqu(Line a[], int &len)
{
    int nlen = 0;
    for(int i=1; i<=len; i++) del[i] = 0;
    for(int i=1; i<=len; i++)
    {
        for(int j=1; j<i; j++) if(!del[j] && (cover(a[i], a[j]) || cover(a[j], a[i]))) 
        {
            a[i].x1 = min(a[i].x1, a[j].x1);
            a[i].x2 = max(a[i].x2, a[j].x2);
            del[j] = 1;
        }
    }
    for(int i=1; i<=len; i++) if(!del[i]) a[++nlen] = a[i];
    len = nlen;
}
void New(pii x)
{
    id[x] = ++tot; f[tot] = tot;
}
int fa(int x) {return x == f[x] ? x : f[x] = fa(f[x]);}
void merge(int x, int y)
{
    x = fa(x), y = fa(y); if(x == y) return;
    f[x] = y;
}
void link(int u, int v)
{
    deg[u]++; deg[v]++; merge(u, v);
}
void LINK()
{
    for(int i=1; i<=ch; i++)
    {
        pii now = pii(h[i].x1, h[i].y);
        if(!id[now]) New(now); int las = id[now];
        for(int j=1; j<=cz; j++) if(check(h[i], z[j]))
        {
            pii now = pii(z[j].y, h[i].y);
            if(!id[now]) New(now);
            if(las != id[now]) link(las, id[now]);
            las = id[now];
        }
        now = pii(h[i].x2, h[i].y);
        if(!id[now]) New(now);
        if(las != id[now]) link(las, id[now]);
    }
    for(int i=1; i<=cz; i++)
    {
        pii now = pii(z[i].y, z[i].x1);
        if(!id[now]) New(now); int las = id[now];
        for(int j=1; j<=ch; j++) if(check(h[j], z[i]))
        {
            pii now = pii(z[i].y, h[j].y);
            if(!id[now]) New(now);
            if(las != id[now]) link(las, id[now]);
            las = id[now];
        }
        now = pii(z[i].y, z[i].x2);
        if(!id[now]) New(now);
        if(las != id[now]) link(las, id[now]);
    }
}
void CUT()
{
    ans = -1;
    for(int i=1; i<=tot; i++) ans += fa(i) == i;
    if(!(m & 1)) return;
    for(int i=1; i<=tot; i++) val[fa(i)] += (deg[i] & 1);
    for(int i=1; i<=tot; i++) if(fa(i) == i) ans += max(val[i]/2-1, 0);
}

int main()
{
    freopen("d.in", "r", stdin);
    freopen("d.out", "w", stdout);
    
    n = read(); m = read();
    for(int i=1; i<=n; i++)
    {
        int a = read(), b = read(), c = read(), d = read();
        if(a == c) z[++cz] = {min(b,d), max(b,d), a};
        else h[++ch] = {min(a,c), max(a,c), b};
    }
    uniqu(z, cz); uniqu(h, ch); sort(h+1, h+1+ch); sort(z+1, z+1+cz);
    LINK(); CUT();
    printf("%d\n", ans);

    return 0;
}

 

posted @ 2022-11-17 15:25  Catherine_leah  阅读(57)  评论(7编辑  收藏  举报
/* */