2022NOIPA层联测12

佛说,退一步海阔天空啊,亲。
可是,当你一步一步退下来,才会发现,你面前那广阔的海洋,高远的天空,可能都不再属于你了。而当你渐渐归顺于庸庸碌碌、汲汲营营的人生,才会懂得,那都是你妥协的代价。

A. 染色

想到了二分图?mex?再模一下发现1234就是答案!4是最小的合数,它的倍数当然也是合数,差4个一定可以分到一组。样例太赞了,否则我还得考虑n等于几的时候是k取4的边界,最小值这不就直接有了吗!

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

using namespace std;

typedef long long ll;
const int maxn = 3e5 + 3;

int n;

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;
}

int main()
{
    freopen("color.in", "r", stdin);
    freopen("color.out", "w", stdout);
    
    n = read();
    if(n == 1)
    {
        printf("1\n");
        printf("1\n"); exit(0);
    }
    if(n == 2)
    {
        printf("1\n");
        printf("1 1\n"); exit(0);
    }
    if(n == 3)
    {
        printf("2\n");
        printf("1 1 2\n"); exit(0);
    }
    if(n == 4)
    {
        printf("2\n");
        printf("1 1 2 2\n"); exit(0);
    }
    if(n == 5)
    {
        printf("3\n");
        printf("1 1 2 2 3\n"); exit(0);
    }
    if(n == 6)
    {
        printf("3\n");
        printf("1 1 2 2 3 3\n"); exit(0);
    }
    printf("4\n");
    for(int i=1; i<=n; i++)
    {
        int col = (i-1) % 4;
        printf("%d ", col+1);
    }
    printf("\n");

    return 0;
}

 

B. 序列

呃,没看懂那个函数形状是怎么搞的……大概都说是三分我就三分吧,好多奇奇妙妙的三分写法……我本来想直接取最大值因为样例是这样的。还有忽然发现要找到这个最大值不用二分,求个和作除法就好了,好2啊。

check就是利用贪心策略,若a[i] < a[j],则b[i] >= b[j],所以确定好均摊的范围剩下的能加的就加。

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

using namespace std;

typedef long long ll;
const int maxn = 2e5 + 3;

ll T, n, m, k, D, sum, l, r, pos, ans, a[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;
}
//贪心策略:若ai <= aj,则bi >= bj
//从小到大枚举a对应的b,每一步都使当前的b大到上限
//我本来以为均摊到最大最优,结果没分了。。事实上均摊成多少需要分
ll check(int x)//初始所有bi相等,分出这个值是多少
{
     ll ret = D-x*sum, ans = k*x+m*x;
     for(int i=1; i<=m; i++)
     {
         if(ret < a[i]) return ans;
         ll p = min(ret/a[i], n-x);//找到给bi加几个1,b每多1乘积和就多一个ai
         ans += p; ret -= a[i] * p;
     }
     return ans;
}

int main()
{
    freopen("array.in", "r", stdin);
    freopen("array.out", "w", stdout);
    
    T = read();
    while(T--)
    {
        n = read(); m = read(); k = read(); D = read();
        sum = 0; ans = 0;
        for(int i=1; i<=m; i++)
        {
            a[i] = read(); sum += a[i];
        }
        sort(a+1, a+m+1);
        l = 0; r = D/sum;
       /* while(r-l>10000)//我鹤了个什么鬼?我不知道这是三分还是二分了。。
        {
            ll mid = (l + r) >> 1;
            if(check(mid) < check(mid+1)) l = mid + 1;
            else r = mid;
        }
        for(ll i=l; i<=r; i++) ans = max(ans, check(i));*/
        while(l <= r)//我还是鹤个正经的三分吧
        {
            if(r - l <= 100)
            {
                for(ll i=l; i<=r; i++) ans = max(ans, check(i));
                break;
            }
            ll lmid = (r - l) / 3 + l;
            ll rmid = r - (r - l) / 3;
            ll al = check(lmid), ar = check(rmid);
            ans = max(ans, max(al, ar));
            if(al < ar) l = lmid + 1;
            else r = rmid - 1;
        }
        printf("%lld\n", ans);
    }

    return 0;
}

 

C. 树上询问

8:01 TLE 75就交上去了,hhh我是第一个提交的,今日开题顺序3214,8:15模T2模不出来做T1去了,8:22交了发现没加freopen,8:24又交了一版A了,至此今天得分的题都拿到了分……

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

using namespace std;

typedef long long ll;
const int maxn = 3e5 + 3;

int n, m, ans, t;
int dep[maxn], fa[maxn], siz[maxn], top[maxn], son[maxn];

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 next, to;
}a[maxn<<1];
int head[maxn], len;

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

void find_heavy_edge(int u, int fat, int depth)
{
    siz[u] = 1;
    dep[u] = depth;
    fa[u] = fat;
    son[u] = 0;
    int maxsize = 0;
    for(int i=head[u]; i; i=a[i].next)
    {
        int v = a[i].to;
        if(dep[v]) 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].next)
    {
        int v = a[i].to;
        if(v == fa[u] || v == son[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;
}

int main()
{
    freopen("query.in", "r", stdin);
    freopen("query.out", "w", stdout);
    
    n = read(); m = read();
    for(int i=1; i<n; i++)
    {
        int x = read(), y = read();
        add(x, y); add(y, x);
    }
    find_heavy_edge(1, 1, 1);
    connect_heavy_edge(1, 1);
    while(m--)
    {
        ans = 0;
        int l = read(), r = read(), lca = LCA(l, r);
        int dis = dep[l] - dep[lca] + dep[r] - dep[lca];
        t = 0;
        while(l != lca)
        {
            l = fa[l]; t++;
            //printf("l = %d t = %d\n", l, t);
            if(t == l) ans++;
        }
        t = dis;
        while(r != lca)
        {
            //printf("r = %d t = %d\n", r, t);
            if(r == t) ans++;
            r = fa[r]; t--;
        }
        printf("%d\n", ans);
    }

    return 0;
}

将链拆为两端a到lca(a, b)与lca(a, b)到b,a到lca(a, b)的答案就是dep[a] - dep[x] = x的x的个数,也就是说dep[x] + x - dep[a]的x的个数,b到lca(a, b)的答案就是dis - (dep[b] - dep[x]) = x的x的个数,dep[x] - x = dep[b] - dis,注意数组下标不能为负数。

套路:将询问离线,做一个树上前缀和,树上差分查找点到根路径上又多少个点满足条件,dfs出x的时候把x的贡献删掉。

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

using namespace std;

typedef long long ll;
const int maxn = 3e5 + 3;

int n, m;
int dep[maxn], fa[maxn], siz[maxn], top[maxn], son[maxn];

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 next, to;
}a[maxn<<1];
int head[maxn], len;

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

void find_heavy_edge(int u, int fat, int depth)
{
    siz[u] = 1;
    dep[u] = depth;
    fa[u] = fat;
    son[u] = 0;
    int maxsize = 0;
    for(int i=head[u]; i; i=a[i].next)
    {
        int v = a[i].to;
        if(dep[v]) 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].next)
    {
        int v = a[i].to;
        if(v == fa[u] || v == son[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;
}

struct que {int id, opt, ad, k;};
vector<que> v[maxn];
int tmp1[maxn*3], tmp2[maxn*3], base, ans[maxn];

void solve(int x)
{
    tmp1[x+dep[x]]++;
    tmp2[dep[x]-x+base]++;
    for(que y : v[x])
    {
        if(y.ad & 1) ans[y.id] += y.opt* tmp1[y.k];
        else ans[y.id] += y.opt * tmp2[y.k+base];
    }
    for(int i=head[x]; i; i=a[i].next)
    {
        int v = a[i].to;
        if(v == fa[x]) continue;
        solve(v);
    }
    tmp1[x+dep[x]]--;
    tmp2[dep[x]-x+base]--;
}

int main()
{
    freopen("query.in", "r", stdin);
    freopen("query.out", "w", stdout);
    
    n = read(); m = read();
    for(int i=1; i<n; i++)
    {
        int u = read(), v = read();
        add(u, v); add(v, u);
    }
    base = n + 5;
    find_heavy_edge(1, 0, 1);
    connect_heavy_edge(1, 1);
    for(int i=1; i<=m; i++)
    {
        int l = read(), r = read(), lc = LCA(l, r);
        int s = dep[l] + dep[r] - dep[lc] - dep[lc];
        v[l].push_back({i, 1, 1, dep[l]});
        v[r].push_back({i, 1, 2, dep[r]-s});
        v[lc].push_back({i, -1, 1, dep[l]});
        v[fa[lc]].push_back({i, -1, 2, dep[r]-s});
    }
    solve(1);
    for(int i=1; i<=m; i++) printf("%d\n", ans[i]);

    return 0;
}

 

D. 网络

n = 3输啥都行直接送分。m <= 20二进制枚举。

我有一种构造方案可以构造n = 4的情况至少剩下两个有电流,先发现构造n = 3的情况一定可以剩下两个,4的话就相当于把第一条需要汇入的线去掉,这个可以正推,每条路的方向只与下一条路涉及哪两个点有关,电流的方向就是躲掉其他情况汇入,因为第一次平衡一定会有一个损失,n = 3的时候要剩两条那以后就不能再有有电流向有电流汇入的情况,那么对于将会有可能成为汇入线的电线,它需要先把它的电流导出到没有电流的那条导线上躲避电流,由于上一步合法,推下去后面的也合法,把涉及到指定要删除的线提前删掉就可以把n = 4变成n = 3。为什么我考虑了n = 3剩两个?因为我看成了向上取整。

然而我没审题,我以为向上就是n变小的方向,向下就是n变大的方向,于是输入的时候比较大小swap了一下,30->10。审题错误*2,想到部分分的解法负正得负……

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

using namespace std;

typedef long long ll;
const int maxn = 5e6 + 3;

int n, m, u[maxn], v[maxn], lim;
bool vis[maxn];

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;
}

void solvex()
{
    printf("YES\n");
    for(int i=1; i<m; i++)
    {
        if(u[i] == u[i+1]||u[i] == v[i+1])
        {
            printf("1"); //vis[u[i]] = 0; vis[v[i]] = 1;
        }
        else 
        {
            printf("0"); //if(vis[v[i]]) vis[v[i]] = 0, vis[u[i]] = 1;
        }
    }
    printf("0\n");
}

void solve3()
{
    printf("YES\n");
    for(int i=1; i<=m; i++)
    {
        printf("0");
    }
    printf("\n");
}

struct node 
{
    int u, v, id;
}ls[maxn];
int cnt;
bool ans[maxn];

void solve4()
{
    printf("YES\n");
    printf("1"); int sp = u[1];
    for(int i=2; i<=m; i++)
    {
        if(u[i] == sp) {ans[i] = 1; continue;}
        if(v[i] == sp) {ans[i] = 0; continue;}
        ls[++cnt].u = u[i], ls[cnt].v = v[i]; ls[cnt].id = i; 
        //printf("ls[%d].u = %d ls[%d].v = %d\n", cnt, ls[cnt].u, cnt, ls[cnt].v);
    }
    for(int i=1; i<cnt; i++)
    {
        if(ls[i].u == ls[i+1].u || ls[i].u == ls[i+1].v)
        {
            ans[ls[i].id] = 1;
        }
        else 
        {
            ans[ls[i].id] = 0;
        }
    }
    for(int i=2; i<m; i++) printf("%d", ans[i]);
    printf("0\n");
}

int Max;
void check(int num)
{
    for(int i=1; i<=n; i++) ans[i] = 1;
    for(int i=1; i<=m; i++)
    {
        if(num & (1<<i-1)) 
        {
            if(ans[u[i]]) ans[u[i]] = 0, ans[v[i]] = 1;
        }
        else 
        {
            if(ans[v[i]]) ans[v[i]] = 0, ans[u[i]] = 1;
        }
    }
    cnt = 0;
    for(int i=1; i<=n; i++)
    {
        if(ans[i]) cnt++;
    }
    if(cnt >= lim)
    {
        printf("YES\n");
        for(int i=1; i<=m; i++)
        {
            if(num & (1<<i-1)) printf("1");
            else printf("0");
        }
        exit(0);
    }
}

void solvesml()
{
    Max = 1 << m;
    for(int i=0; i<Max; i++)
    {
        check(i);
    }
    printf("NO\n");
}

int main()
{
    freopen("network.in", "r", stdin);
    freopen("network.out", "w", stdout);
    
    n = read(); m = read();
    //for(int i=1; i<=n; i++) vis[i] = 1;
    for(int i=1; i<=m; i++)
    {
        u[i] = read(); v[i] = read();
        //if(u[i] > v[i]) swap(u[i], v[i]);
    }
    if(n == 3) 
    {
        solve3(); exit(0);
    }
    if(n == 4)
    {
        solve4(); exit(0);
    }
    if(m <= 20)
    {
        lim = n / 2;
        solvesml(); exit(0);
    }
    solvex();

    return 0;
}

听说过一字千金吗?就是写得太好一个字都改不了。用它来形容Chen_jr的题解正好qwq我改了是因为懒的打那么多字qwq

我们只需要保证带电的>=n/2,每一次的平衡,实际上是一个x,y不能同时带电的操作,于是我们把满足 不能同时带电 的两根电线看成一个二元组。初始随便定,先正推确定一个合法的终止状态和过程中的状态变化,再倒推一遍撤销每次配对的影响。

出现一条x,y的平衡器是,假设原来的配对为(x, a)(y, b),此时x,y不能同时带电,所以新的配对为(x, y)(a, b)。推回到一条边x, y时,如果此时y带电(经过x,y这条边之后y还有电),那么为1,否则为0。撤销该次配对时,由于只能改变x, y的电性,所以需要根据之前的x, y的电性来重新确定x, y。

以上是涉及到4根电线的情况,3根电线同理。

代码都是鹤!T3也是鹤!!

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

using namespace std;

typedef long long ll;
const int maxn = 5e6 + 3;

struct edge {int x, y;} e[maxn];
bool elc[maxn];
int n, m, oth[maxn], ans[maxn], opt[maxn][2];

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;
}

int main()
{
    freopen("network.in", "r", stdin);
    freopen("network.out", "w", stdout);
    
    n = read(); m = read();
    for(int i=1; i<=m; i++) e[i].x = read(), e[i].y = read();
    for(int i=1; i<=n; i+=2) oth[i] = i+1;
    if(n & 1) oth[n] = n;
    for(int i=2; i<=n; i+=2) oth[i] = i-1;
    for(int i=1; i<=m; i++)
    {
        int x = e[i].x, y = e[i].y;
        int ox = oth[x], oy = oth[y];
        if(ox == y) continue;
        if(x == ox)
        {
            oth[y] = x;
            oth[x] = y;
            oth[oy] = oy;
            opt[i][1] = oy;
        }
        else if(y == oy)
        {
            oth[y] = x;
            oth[x] = y;
            oth[ox] = ox;
            opt[i][0] = ox;
        }
        else 
        {
            oth[ox] = oy; oth[oy] = ox;
            oth[x] = y; oth[y] = x;
            opt[i][0] = ox, opt[i][1] = oy;
        }
    }
    for(int i=1; i<=n; i++) elc[i] = !elc[oth[i]];
    for(int i=m; i>=1; i--)
    {
        int x = e[i].x, y = e[i].y;
        int ox = opt[i][0], oy = opt[i][1];
        if(elc[y]) ans[i] = 1;
        if(ox && oy)
        {
            if(elc[ox]) elc[x] = 0, elc[y] = 1;
            else elc[x] = 1, elc[y] = 0;
        }
        else if(ox)
        {
            elc[x] = 0;
            elc[ox] = elc[y] = 1;
        }
        else 
        {
            elc[y] = 0;
            elc[oy] = elc[x] = 1;
        }
    }
    printf("YES\n");
    for(int i=1; i<=m; i++) printf("%d", ans[i]);

    return 0;
}

dfs出60分的大佬简直太巨辣%%%%%我只有模%%%%%

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

using namespace std;

typedef long long ll;
const int maxn = 5e6 + 3;

int u[maxn], v[maxn], n, m;
bool ele[maxn], as[maxn];

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;
}

bool dfs(int now, int lef)
{
    if(lef < (n+1)/2) return 0;
    if(now > m) return 1;
    if(ele[u[now]] && ele[v[now]])
    {
        as[now] = 1;
        ele[u[now]] = 0;
        if(dfs(now+1, lef-1)) return 1;
        as[now] = 0;
        ele[u[now]] = 1, ele[v[now]] = 0;
        if(dfs(now+1, lef-1)) return 1;
        ele[u[now]] = ele[v[now]] = 1;
    }
    else if(ele[u[now]] && !ele[v[now]])
    {
        as[now] = 0;
        if(dfs(now+1, lef)) return 1;
        as[now] = 1;
        ele[u[now]] = 0; ele[v[now]] = 1;
        if(dfs(now+1, lef)) return 1;
        ele[u[now]] = 1; ele[v[now]] = 0;
    }
    else if(!ele[u[now]] && ele[v[now]])
    {
        as[now] = 1;
        if(dfs(now+1, lef)) return 1;
        as[now] = 0;
        ele[v[now]] = 0; ele[u[now]] = 1;
        if(dfs(now+1, lef)) return 1;
        ele[v[now]] = 1; ele[u[now]] = 0;
    }
    else 
    {
        as[now] = 0;
        if(dfs(now+1, lef)) return 1;
    }
    return 0;
}

int main()
{
    freopen("network.in", "r", stdin);
    freopen("network.out", "w", stdout);
    
    n = read(); m = read();
    for(int i=1; i<=m; i++) u[i] = read(), v[i] = read();
    fill(ele+1, ele+1+n, 1);
    dfs(1, n);
    printf("YES\n");
    for(int i=1; i<=m; i++) printf("%d", as[i]);

    return 0;
}
posted @ 2022-10-19 19:54  Catherine_leah  阅读(15)  评论(0编辑  收藏  举报
/* */