『模拟赛』暑假集训CSP提高模拟26

Rank

打得一般,倒数第二场了。。

image

A. 博弈

直接搬了牛客的一套题。

一眼没思路,模了一会放弃直接去打 T2 了,后来把 \(\mathcal{O(n^2)}\) 暴力 打了拿 30pts。

正解用到了异或哈希。首先确定合法的数量即为总对数 \(\frac{n(n-1)}{2}\) 减去不合法的数量,而比较显然的,不合法的判断条件为二者路径上每种长度边权均为偶数条,而相同的数异或和为 0,我们根据这个性质,算出每个点到跟的异或距离,并记录下每个距离的点数,每次增加点数前将答案减去当前距根等于该距离的点数,简单想想就能证出正确性。

当然,用哈希的原因是若边权很小,有可能出现异或后距离等于一个不相等的长度,如 \(3 \operatorname{xor} 5=6\),为尽可能避免这种情况,我们用随机数,将不会实际引用的边权重新赋值为一个更大的随机数,这样发生冲突的概率就很小了。

卡常的话就数组离散化,map 还是过于慢了。

点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x)=(y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define lx ll
inline lx qr()
{
	char ch=getchar();lx x=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
	return x*f;
}
#undef lx
#define qr qr()
#define fi first
#define se second
const int Ratio=0;
const int N=5e5+5;
const int inf=1e9;
int n,B;
int hh[N],to[N<<1],ne[N<<1],cnt;
ull w[N<<1],dis[N],ans;
mt19937_64 rng(random_device{}());
map<ull,ull>mp,g;
namespace Wisadel
{
    void Wadd(int u,int v,ull va)
    {
        to[++cnt]=v;
        w[cnt]=va;
        ne[cnt]=hh[u];
        hh[u]=cnt;
    }
    void Wdfs(int u,int fa,ull va)
    {
        dis[u]=dis[fa]^va;
        ans-=g[dis[u]];
        g[dis[u]]++;
        for(int i=hh[u];i!=-1;i=ne[i])
        {
            int v=to[i];
            if(v==fa) continue;
            Wdfs(v,u,w[i]);
        }
    }
    short main()
    {
        int T=qr;
        while(T--)
        {
            n=qr;cnt=0;ans=1ll*n*(n-1)/2;
            fill(hh+1,hh+1+n,-1);
            g.clear();
            fo(i,1,n-1)
            {
                ll a=qr,b=qr,c=qr;
                if(!mp[c]) mp[c]=rng();
                Wadd(a,b,mp[c]),Wadd(b,a,mp[c]);
            }
            Wdfs(1,0,0);
            printf("%lld\n",ans);
        }
        return Ratio;
    }
}
int main(){return Wisadel::main();}

B. 跳跃

shabi

一眼有思路,一个小时打出完整代码,自信到最后改个小错交了,结果半 RE 半 WA 保龄。

所以只能考虑正解,记录数对 \(f_i\) 包含从 \(1\) 跳到 \(i\) 的最小步数和该步数下能得到的最大分数,还要求出在点 \(i\) 向左跳能得到的最大分数,转移时枚举 \(i\) 之前的所有点,找到从该点跳到目标点的最小步数和得分,然后更新就行了,时间复杂度 \(\mathcal{O(n^2)}\)

点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x)=(y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
	char ch=getchar();lx x=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
	return x*f;
}
#undef lx
#define qr qr()
#define fi first
#define se second
const int Ratio=0;
const int N=1000+5;
const ll inf=1e18;
int n,k;
ll a[N],sum[N];
ll f[N],ans;
pair<ll,ll>ff[N];
namespace Wisadel
{
    short main()
    {
        int TT=qr;
        while(TT--)
        {
            n=qr,k=qr;ans=0;
            fo(i,1,n) a[i]=qr,sum[i]=sum[i-1]+a[i];
            ll zc=0;
            fo(i,1,n)
            {
                ff[i]={inf,0};f[i]=0;
                f[i]=sum[i]-zc;zc=min(zc,sum[i]);
                if(sum[i]>=0) ff[i]={1,sum[i]};
            }
            ff[1]={0,0};
            ans=max(ans,1ll*sum[1]*k);
            fo(i,2,n)
            {
                fo(j,1,i-1)
                {
                    ll minst=2+ff[j].fi,mamark=ff[j].se+2*f[j]+sum[i]-sum[j];
                    if(ff[j].fi>k||ff[j].se<0) continue;
                    if(mamark<0)
                    {
                        if(f[j]<=0) continue;
                        minst+=(-mamark+f[j]-1)/f[j];
                        mamark+=(-mamark+f[j]-1)/f[j]*f[j];
                    }
                    if(minst%2==0) minst++,mamark+=f[j];
                    if(minst<ff[i].fi) ff[i].fi=minst,ff[i].se=mamark;
                    else if(minst==ff[i].fi) ff[i].se=max(ff[i].se,mamark);
                }
                if(ff[i].fi<=k) ans=max(ans,ff[i].se+(k-ff[i].fi)*f[i]);
            }
            printf("%lld\n",ans);
        }
        return Ratio;
    }
}
int main(){return Wisadel::main();}

C. 大陆

原[SCOI2005] 王室联邦

赛时想到了正解的做法,但嗓子肿了并有些困所以没精力实现,于是暴力特判 + 乱搞拿到 58pts。

先考虑若 \(B = 1\),把所有城市各自为省即可;若 \(3\times B\ge n\),将所有城市为一个省即可。

其他情况,递归至叶子结点向上操作,现将叶子结点们都置入同一个省,若数量 \(\ge B\) 则立即开新的省并将原来的省的省会置为该点;处理最后未成功分省的城市,直接将它们都放到上一个省内即可,因为我们的断省原则使得这样操作不会超过省最多城市的限制。整个过程只进行一遍 dfs,时间复杂度为 \(\mathcal{O(n)}\)

点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x)=(y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
	char ch=getchar();lx x=0,f=1;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
	return x*f;
}
#undef lx
#define qr qr()
#define fi first
#define se second
const int Ratio=0;
const int N=1e4+5;
const int inf=1e9;
int n,B;
int hh[N],to[N<<1],ne[N<<1],cnt;
int siz[N],bl[N],tot;
stack<int>st;
struct sheng
{
    int sh,sum;
}sheng[N];
namespace Wisadel
{
    void Wadd(int u,int v)
    {
        to[++cnt]=v;
        ne[cnt]=hh[u];
        hh[u]=cnt;
    }
    void Wdfs(int u,int fa)
    {
        int L=st.size();
        for(int i=hh[u];i!=-1;i=ne[i])
        {
            int v=to[i];
            if(v==fa) continue;
            Wdfs(v,u);
            if(st.size()-L>=B)
            {
                sheng[++tot].sh=u;
                while(st.size()>L)
                    bl[st.top()]=tot,st.pop();
            }
        }
        st.push(u);
    }
    short main()
    {
        // freopen(".in","r",stdin),freopen(".out","w",stdout);
        n=qr,B=qr;
        memset(hh,-1,sizeof hh);
        fo(i,1,n-1)
        {
            int a=qr,b=qr;
            Wadd(a,b),Wadd(b,a);
        }
        if(B==1)
        {
            printf("%d\n",n);
            fo(i,1,n) printf("%d ",i);printf("\n");
            fo(i,1,n) printf("%d ",i);printf("\n");
            return Ratio;
        }
        if(3*B>=n)
        {
            printf("1\n");
            fo(i,1,n) printf("1 ");printf("\n");
            printf("1\n");
            return Ratio;
        }
        Wdfs(1,0);
        if(!tot) sheng[++tot].sh=1;
        while(st.size()) bl[st.top()]=tot,st.pop();
        printf("%d\n",tot);
        fo(i,1,n) printf("%d ",bl[i]);printf("\n");
        fo(i,1,tot) printf("%d ",sheng[i].sh);
        return Ratio;
    }
}
int main(){return Wisadel::main();}

D. 排列

一眼没啥思路遂直接暴力 40pts 后润。

正解是 FHQ-Treap,一眼看出码量,咕咕咕。

倒数第二场了,转眼暑假都过去了,真快,感觉还啥也没干啥也没玩啥也没学就结束了。

但其实往回看看,自己的成绩确实是在隐形中进步了,这就是学长说的打模拟赛能在无意识中提升实力吧。

明天最后一场了,(后天就放假了好耶,尽全力打吧,争取画上一个叹号或者圆满的句号吧。


完结撒花~

posted @ 2024-08-21 21:28  DrRatio  阅读(34)  评论(0编辑  收藏  举报