[2027届]NOIP2024模拟赛#5

%%% Larunatrecy

比赛链接

榜:

image

打得还行吧。

T1

光理解题意就看了10min,理解以后写了写有手就行的暴力。

赛后发现输出 -1 能多拿10分,惨痛错过呜呜呜。

正解的话,我们给每个节点定义两个指标:

  • \(a:\) 即使加入一条入边也依旧存在一种合法的 \(W\)
  • \(b:\) 即使加入一条出边也依旧存在一种合法的 \(W\)

那么,对于一个 至少有一条边没有确定方向的节点 \(x\)

  • 如果 \(a,b\) 都不满足,那么直接无解。
  • 如果 \(a,b\) 恰好满足一个,那么该节点所有未确定方向的边都可以确定方向。

注意第二种节点会导致一些别的节点也变成这两种状态之一,这是一个迭代的过程。

完成迭代后,此时每个节点要么 \(a,b\) 都满足,要么它的所有边都已经定向。

接下来,我们考虑如果没有一类节点,那么一定存在一组解,也很简单:

  • 任选一个节点作为根,并把他的儿子节点任意定向,然后递归子树,那么对于每个递归到节点,如果都已经定向就无所谓,否则因为 \(a,b\) 都满足,那么父亲的边怎么定向都是合法的,因此不会出问题。

那么做法呼之欲出:按顺序遍历每条边,如果已经定向就定向,否则这条边可以任意定向,迭代这个过程即可。

复杂度 \(\mathcal{O}(n)\)

T2

唯一过掉的题。

一开始对于两个性质,分别跑联通块和乘法原理可以搞到40分的优秀成绩。

然后根据性质想正解,可以把原图按照高度阻隔分成多个块,每一个块完全包含低一级的块。现在要求的就是有多少种用互不包含额度块覆盖原图的方法。

由于块可以抽象成一棵树,所以不难想到 Kruscal 重构树,然后 DP式子稍微一推就出来了,每个节点可以由两个儿子节点分别计算合并而来。

然后第一次交的时候数组开小了,幸好检查了,大汗)

lg上面竟然是紫题,经验+1。

点击查看代码
#include<bits/stdc++.h>
// #include <ext/pb_ds/assoc_container.hpp>
// #include <ext/pb_ds/tree_policy.hpp>
#define int long long
using namespace std;
// using namespace  __gnu_pbds;
// tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> tr;//从小到大
// int findnum(int k){auto it=tr.find_by_order(k-1);return ((it!=tr.end())?(*it):1e9+7);}//查元素
// int findrank(int x){return tr.order_of_key(x)+1;}//查排名
inline int read()
{
	int w = 1, s = 0; char ch = getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch)){s=s*10+(ch-'0');ch=getchar();}
	return w*s;
}
const int mod=998244353;
const int maxn=1e6+10;
const int inf=1e9+7;
int qw(int a, int b)
{
	int ans=1;a%=mod;
	while(b)
	{
		if(b&1)ans=ans*a%mod;
		b>>=1;
		a=a*a%mod;
	}
	return ans;
}
int n,m,H;
struct no
{
    int y,d;
};
vector<no> G[maxn];
bool vis[maxn];
void dfs1(int x)
{
    vis[x]=1;
    for(auto i : G[x])
    {
        int y=i.y,d=i.d;
        if(d==1||vis[y])continue;
        dfs1(y);
    }
}
void Sub1()
{
    int num=0;
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            dfs1(i);
            num++;
        }
    }
    cout<<qw(2,num);
    return ;
}
void Sub2(int D)
{
    if(H<=D)
    {
        cout<<qw(H+1,n);
        return ;
    }
    cout<<(qw(D+1,n)+(H-D)%mod)%mod;
    return ;
}
vector<int> GG[maxn];
int tot=0;
int cnt[3000][3000];
struct bi
{
    int x,y,v;
    inline friend bool operator < (bi x,bi y)
    {
        return x.v<y.v;
    }
};
vector<bi> mm;
int fa[maxn],d[maxn];
int gf(int x){return fa[x]==x?x:fa[x]=gf(fa[x]);}
void dfs3(int x,int h,int d)
{
    cnt[h][x]=d;
    for(auto i : G[x])
    {
        int y=i.y,dd=i.d;
        if(dd>=h||cnt[h][y])continue;
        dfs3(y,h,d);
    }
}
int dp[maxn],c[2];
void dfs(int x,int fa)
{
    int t=0;
    dp[x]=1;
    for(auto y : GG[x])
    {
        if(y==fa)continue;
        c[++t]=y;
        dfs(y,x);
    }
    dp[x]=( ( d[x] - d[c[1]] ) + dp[c[1]] ) % mod
         *( ( d[x] - d[c[2]] ) + dp[c[2]] ) % mod;
}
void Sub3()
{
    int ans=0;tot=n;
    for(int i=1;i<=m+n;i++)fa[i]=i;
    for(auto i : mm)
    {
        int x=i.x,y=i.y,v=i.v;
        int fx=gf(x),fy=gf(y);
        if(fx==fy)continue;
        tot++; d[tot]=v;
        fa[fx]=fa[fy]=tot;
        GG[tot].push_back(fx);
        GG[tot].push_back(fy);
        GG[fx].push_back(tot);
        GG[fy].push_back(tot);
        dp[tot]=
        ( dp[fx] + ( d[tot] - d[fx] ) ) % mod
       *( dp[fy] + ( d[tot] - d[fy] ) ) % mod;
    }
    ans=(dp[tot]-d[tot])%mod;
    // dfs(tot,0);
    cout<<(ans+H)%mod;
}
signed main()
{
#ifdef Lydic
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
#else
    freopen("rain.in","r",stdin);
    freopen("rain.out","w",stdout);
#endif
    
    cin>>n>>m>>H;
    int Su2=0;bool f=1;
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read(),v=read();
        if(i==1)Su2=v;
        else if(v!=Su2)f=0;
        G[x].push_back({y,v});
        G[y].push_back({x,v});
        mm.push_back({x,y,v});
    }
    if(H==1){Sub1();return 0;}
    if(f){Sub2(Su2);return 0;}
    sort(mm.begin(),mm.end());
    for(int i=1;i<=m+n;i++)dp[i]=1;
    Sub3();
    return 0;
}

T3

图上邻域区间修改区间查询。

暴力直接模拟。

题目说这是数据结构,但是我把所有数据结构扒出来也没有想到是什么。

就写了暴力分。

正解是一个很神奇的分块,设 \(S\)\(R(i)\) 顺次拼起来得到的长度为 \(2m\) 的序列,那么问题简化为:

  • 给定 \([l,r]\),对于 \(i\in [l,r]\),令 \(w_{S_i}=w_{S_i}+v\)
  • 给定 \([l,r]\),求出 \(\sum_{i\in [l,r]}w_{S_i}\)

把序列 \(S\) 分块,考虑每种贡献。

  • 整块/散块对整块,预处理 \(f_{i,j}\) 表示第 \(i\) 个块与前 \(j\) 个位置的贡献,那么询问的时候枚举块 \(i\) 就能得到贡献。
  • 散块对散块,暴力修改。
  • 整块对散块,与第一种类似。

复杂度 \(\mathcal{O}((m+q)\sqrt m)\),需要离线做到线性空间。

T4

题目压根没懂,说的跟shi一样(好像不太好)。

题目看懂以后会一个10分DP部分分,但是是赛后。

CF3500的评分,做出来直接是红黑名阿伟。

正解是一个很复杂的二进制按位区间DP,能做到 \(\mathcal{O}(n^3m)\),但是根本不会。

posted @ 2024-08-23 16:59  Redamancy_Lydic  阅读(20)  评论(0编辑  收藏  举报