筑基赛Day 3总结及题目简单梳理

没什么好总结的,纯纯炸掉,全看T5了,还没写出来,现在想起来就是泪啊、

T1

NOIP春季联赛的时候写过了,可以看看学长的题解,在这里就不多提了。

T2 Barn Tree

link

我们已知每个节点权值,则可以知道每个节点最终的权值 \(k=tot/n\),其中 \(tot\) 为权值之和。

我们可以考虑去掉一条边,则树被分成两个子图,这两个子图互相独立。

设子图内节点个数是 \(siz\),总共需要 \(siz×k\) 个干草捆,所以其内部干草捆总数 \(hs\) 一定不变。

所以可以判断出一个子图干草捆总数过剩还是不足,还是正好。

然后就可以计算出每条边是否使用,运送的方向,运送的量。

考虑树形DP,维护 \(siz_u\)\(hs_u\)

然后考虑如何输出方案。显然,拓扑序将会是一个正确的输出顺序。因为这个方法保证了每条必须使用的边都被使用了 1 次,且没有使用其他的边,所以命令数一定是最小的。

代码较为简单。

点击查看代码
#include<bits/stdc++.h>
#define int long long

#define pb push_back


using namespace std;

const int N=2e5+100;

int n,k;
int a[N];
vector<int> G[N];

struct NODE{
    int v,w;
};

int siz[N],din[N];
int hs[N];
vector<NODE> ng[N];

void dfs(int u,int fa)
{
    siz[u]=1,hs[u]=a[u];
    for(auto v:G[u])
    {
        if(v==fa)   continue;
        dfs(v,u);
        siz[u]+=siz[v],hs[u]+=hs[v];
    }
    int d=hs[u]-siz[u]*k;
    if(d>0) ng[u].pb({fa,d});
    else if(d<0) ng[fa].pb({u,-d});
}

void topsort()
{
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        cnt+=ng[i].size();
        for(auto j:ng[i])
            din[j.v]++;
    }
    cout<<cnt<<endl;
    queue<int> q;
    for(int i=1;i<=n;i++)
        if(!din[i])
            q.push(i);
    while(q.size())
    {
        int t=q.front();q.pop();
        for(auto k:ng[t])
        {
            cout<<t<<" "<<k.v<<" "<<k.w<<endl;
            if(!--din[k.v]) q.push(k.v);
        }
    }
}

signed main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i],k+=a[i];
    k/=n;
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        G[u].pb(v),G[v].pb(u);
    }

    dfs(1,0),topsort();

    return 0;
}

T3 Taxi

link

一眼贪心题。

显然,总路程=空载路程+载人(牛)路程。显然载人(牛)路程不变,所以总路程最短即空载路程最短。

点击查看代码
#include<bits/stdc++.h>
#define int long long


using namespace std;

const int N=1e5+100;

int n,m;
int x[N],y[N];
int ans;

signed main()
{
    freopen("taxi.in","r",stdin);
    freopen("taxi.out","w",stdout);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>x[i]>>y[i],ans+=abs(x[i]-y[i]);
    x[++n]=m,y[n]=0;
    sort(x+1,x+n+1),sort(y+1,y+n+1);
    for(int i=1;i<=n;i++)
        ans+=abs(x[i]-y[i]);
    return cout<<ans<<endl,0;
}

T4 Promotion Counting

link

题意:求对于每个节点,其子树中权值比自己大的节点个数

正解显然是不会的,看到二位数点(今天晚上看看吧)或者线段树合并的Tag就知道对于我这种蒟蒻来说必须剑走偏锋。

考虑分块。

我们显然可以读入数据,然后对其进行离散化。然后通过DFS求出自己最后的下属。然后分块,在块内根据权值排序,询问时用自己为 \(l\),以自己最后的下属为 \(r\),直接二分该块。代码非常简单。

点击查看代码
#include<bits/stdc++.h>
#define int long long

#define pb push_back


using namespace std;

const int N=1e5+100;

int n,t;
struct NODE{
    int num,id,pos;
}dt[N];
vector<int> G[N];

int ys[N];
void lsh()
{
    for(int i=1;i<=n;i++)   ys[i]=dt[i].num;
    sort(ys+1,ys+n+1);
    int tmp=unique(ys+1,ys+n+1)-ys-1;
    for(int i=1;i<=n;i++)
        dt[i].num=lower_bound(ys+1,ys+tmp+1,dt[i].num)-ys;
}

int tail[N],idx;
int dfs(int x)
{
    dt[x].id=++idx;
    if(G[x].size()==0)  return tail[x]=x;
    for(auto k:G[x])
    {
        int j=dfs(k);
        if(dt[j].id>dt[tail[x]].id) tail[x]=j;
    }
    return tail[x];
}

int fz[N];
int L[N],R[N];
int pos[N],num[N];
int ans[N];

int ask(int l,int r,int k)
{
    if(l>r) return 0;
    int p=pos[l],q=pos[r],res=0;
    if(p==q)
    {
        for(int i=l;i<=r;i++)
            if(dt[i].num>=k)
                res++;
        return res;
    }
    for(int i=l;i<=R[p];i++)
        if(dt[i].num>=k)
            res++;
    for(int i=p+1;i<q;i++)
        res+=R[i]+1-(lower_bound(num+L[i],num+R[i]+1,k)-num);
    for(int i=L[q];i<=r;i++)
        if(dt[i].num>=k)
            res++;
    return res;
}

signed main()
{
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    cin>>n;t=sqrt(n);
    for(int i=1;i<=n;i++)
        cin>>dt[i].num,dt[i].pos=i;
    for(int i=2;i<=n;i++)
    {
        int x;
        cin>>x;
        G[x].pb(i);
    }

    lsh(),dfs(1);
    sort(dt,dt+n+1,[&](NODE a,NODE b){
         return a.id<b.id;
    });

    //for(int i=1;i<=n;i++)   cout<<tail[i]<<endl;

    for(int i=1;i<=n;i++)   fz[dt[i].pos]=dt[i].id;
    for(int i=1;i<=t;i++)
        L[i]=(i-1)*t+1,R[i]=i*t;
    if(R[t]<n)
        t++,L[t]=R[t-1]+1,R[t]=n;
    for(int i=1;i<=t;i++)
    {
        for(int j=L[i];j<=R[i];j++)
            pos[j]=i,num[j]=dt[j].num;
        sort(num+L[i],num+R[i]+1);
    }
    for(int i=1;i<=n;i++)
        ans[dt[i].pos]=ask(i+1,fz[tail[dt[i].pos]],dt[i].num+1);
    for(int i=1;i<=n;i++)
        cout<<ans[i]<<endl;

    return 0;
}

T5 Circular Barn

link

还是推荐官方题解

博弈论

通过观察(打表)发现 Farmer John获胜当且仅当 \(a\) 不能被 \(4\) 整除,所以我们可以显然知道John一定会剪掉 \(1\)\(2\)\(3\)。而如果\(a\)可以被 \(4\) 整除的时候,显然Farmer Nhoj获胜。

因此就可以确定策略:选择一个与 \(a\)\(4\) 同余的有效值,并相应地减少 \(a\)

失败者希望最大化游戏的回合数,而获胜者希望最小化游戏的回合数,那么游戏将在多少回合后结束?当a1为偶数时,我们可以通过归纳法证明答案恰好是 \(a/2\)。否则,获胜玩家希望选择一个与 \(a\)\(4\) 同余的最大素数。如果该素数为 \(p\),那么游戏将需要 \(1+(a-p)/2\) 回合。

然后跑埃氏筛就可以了。

点击查看代码
#include<bits/stdc++.h>
#define int long long


using namespace std;

const int N=5e6+100;

bool sg[N];
int max_mod4[N],min_turns[N];

void init()
{
    max_mod4[0]=2,max_mod4[1]=1,max_mod4[2]=2,max_mod4[3]=3;
    min_turns[0]=0,min_turns[1]=1;
    for(int i=2;i<N;i++)
    {
        if(!sg[i])
        {
            for(int j=i;j<N;j+=i)
                sg[j]=true;
            max_mod4[i%4]=i;
        }
        min_turns[i]=(i-max_mod4[i%4])/2+1;
    }
    return;
}

int n;
void work()
{
    cin>>n;
    int ans=N;
    for(int i=1;i<=n;i++)
    {
        int x;
        cin>>x;
        if(min_turns[x]/2<ans/2)    ans=min_turns[x];
    }
    if(ans&1)   return cout<<"Farmer John"<<endl,void();
    return cout<<"Farmer Nhoj"<<endl,void();
}

signed main()
{
    freopen("barn.in","r",stdin);
    freopen("barn.out","w",stdout);
    init();
    int T;
    cin>>T;
    while(T--)  work();
    return 0;
}

一些小问题

小小的CE小问题

碰到了一种编译问题,在本地运行时合法的,并且可以得到正确答案,但是在 洛谷 或者 123OJ 上交就是CE。

CE截图如下:
luogu
或者
123OJ

然后我去分析原因,发现我的代码里面有一个对全局数组赋初值的操作。
像这样

虽然但是我并不知道这到底是什么问题,但是改成这样就可以通过了。

像这样

虽然现在还不知道是什么原理,但是为了防止莫名其妙的CE,以后还是不要这样定义数组了。。。

C++强制提升栈空间

这个问题在NOIP2024的时候就碰到了,今天又碰到了一次。可能在CCF的机子上可以跑,但是你自己跑大样例的时候可以很方便看。

Dev-cpp 手动开栈:

工具 → 编译选项 → 编译器选项 → 在编译时加入如下命令(这里记得打 √ )

在框里添加相关命令即可(134217728=128∗1024∗1024即128MB的空间。)。

-Wl,--stack=134217728

posted @ 2025-02-24 18:39  袍蚤  阅读(2)  评论(0编辑  收藏  举报