首页 > 编程语言 >2024“钉耙编程”中国大学生算法设计超级联赛(9)

2024“钉耙编程”中国大学生算法设计超级联赛(9)

时间:2024-08-16 19:27:26浏览次数:12  
标签:钉耙 CI return int 编程 2024 query end now

Preface

最后一场 HDU 多校了,前期一直犯病但也堪堪签了前六题,但后期又是酣畅淋漓的后期三开三卡

我写 04,祁神写 09,徐神写 10,最后一个没调出来,赛后祁神和徐神都发现了很多修改点但因为题目还没公开、数据和题解也没法,就先坑着之后再来补了


树异或价值

首先不难发现 \(k\) 位这个限制其实没啥用,我们只要求出一位的情况最后做 \(k\) 次幂即可

这个式子很显然可以看成在某个点 \(x\) 的不同子树内选两个 \(a_i\) 不同的点,并产生 \(dep_x\) 的贡献

不难发现我们需要让子树内 \(0/1\) 的个数尽可能接近才能最大化贡献,因此不妨进行 DP

令 \(f_{x,-1/0/1}\) 表示在 \(x\) 的子树内,\(0\) 的个数减去 \(1\) 的个数为 \(-1/0/1\) 时的方案数,转移就类似于树上背包

但要注意虽然后面一维的范围很小,但遇到菊花图之类的可能会变得很大,因此要套个分治 NTT 优化下

#include <bits/stdc++.h>

// using std::bit_ceil; // C++20
// using std::countr_zero; // C++20

inline int bit_ceil(int a) {
    int i;
    for(i = 1; i < a; i <<= 1);
    return i;
}

inline int countr_zero(int a) {
    return __builtin_ctz(a);
}

using llsi = long long signed int;
constexpr int mod = 998244353;

template<
    typename I,
    typename LL,
    I mod,
    I g
> struct ntpoly_ns {
    using poly = std::vector<I>;
    
    static constexpr I ksm(I a, I b) {
        I c(1);
        while(b) {
            if(b & I(1)) c = LL(c) * a % mod;
            a = LL(a) * a % mod;
            b >>= 1;
        }
        return c;
    }

    static constexpr I gi = ksm(g, mod - 2);

    std::vector<int> rev;
    uint32_t n;

    void prep(int len) {
        n = bit_ceil(uint32_t(len));
        rev.resize(n); rev[0] = 0;
        int t = countr_zero(n) - 1;
        for(int i = 1; i < n; ++i) rev[i] = rev[i >> 1] >> 1 | (i & 1) << t;
    }

    void ext_zero(poly &a, int nlen) {
        uint32_t c = a.size(); if(c >= nlen) return ;
        a.resize(nlen);
        std::fill(a.begin() + c, a.end(), I(0));
    }

    void NTT(I *a, int on) {
        for(int i = 1; i < n; ++i) if(rev[i] > i) std::swap(a[i], a[rev[i]]);

        for(int d = 1; d < n; d <<= 1) {
            I wi = ksm(on > 0 ? g : gi, (mod - 1) / (2 * d));
            for(int j = 0; j < n; j += 2 * d) {
                I w(1);
                for(int k = j; k < j + d; ++k, w = LL(wi) * w % mod) {
                    I x = a[k], y = LL(w) * a[k + d] % mod;
                    a[k]     = (x + y) % mod;
                    a[k + d] = (x + mod - y) % mod;
                }
            }
        }

        if(on < 0) {
            LL inv(ksm(n, mod - 2));
            for(int i = 0; i < n; ++i) a[i] = LL(a[i]) * inv % mod;
        }
    }

    void NTT(poly &a, int on) {
        ext_zero(a, n);
        NTT(a.data(), on);
    }

    poly& mult(poly &a, poly &b) {
        if(a.size() + b.size() < 30) {
            poly c(a.size() + b.size() - 1, 0);
            for(int i = 0; i < a.size(); ++i)
                for(int j = 0; j < b.size(); ++j)
                    c[i + j] = (c[i + j] + LL(a[i]) * b[j]) % mod;
            return a = c;
        }
        prep(a.size() + b.size());
        NTT(a, 1);
        NTT(b, 1);
        for(int i = 0; i < n; ++i) a[i] = LL(a[i]) * b[i] % mod;
        NTT(a, -1);
        return a;
    }

    poly& div_mult(poly *begin, poly *end) {
        if(begin + 1 == end) return *begin;
        poly *mid = begin + (end - begin) / 2;
        poly &left = div_mult(begin, mid);
        poly &right = div_mult(mid, end);
        return mult(left, right);
    }

    poly& div_mult(std::vector<poly> &polys) {
        return div_mult(polys.data(), polys.data() + polys.size());
    }
};

ntpoly_ns<int, llsi, mod, 3> ntpoly;
using poly = ntpoly_ns<int, llsi, mod, 3>::poly;

constexpr int $n = 200005;
std::vector<int> ch[$n];
int siz[$n], fa[$n], dp[$n][3];

void dfs(int cur) {
    siz[cur] = 1;
    std::vector<poly> polys = {poly{1, 0, 1}}; 
    for(auto ch: ch[cur]) {
        dfs(ch);
        siz[cur] += siz[ch];
        polys.push_back(poly{dp[ch][0], dp[ch][1], dp[ch][2]});
    }
    poly &c = ntpoly.div_mult(polys);

    int center = polys.size();
    dp[cur][0] = c[center - 1];
    dp[cur][1] = c[center];
    dp[cur][2] = c[center + 1];
    return ;
}

void work() {
    int n, k; std::cin >> n >> k;
    for(int i = 1; i <= n; ++i) ch[i].clear();
    for(int i = 2; i <= n; ++i) {
        std::cin >> fa[i];
        ch[fa[i]].push_back(i);
    }
    dfs(1);
    llsi ans = llsi(dp[1][0]) + dp[1][1] + dp[1][2];
    std::cout << ntpoly.ksm(ans % mod, k) << char(10);
    return ;
}

int main() {
    std::ios::sync_with_stdio(false);
    int T; std::cin >> T; while(T--) work();
    return 0;
}


树上询问

和之前 CF做过的一个题 的思路可以说是一模一样

考虑确定树上路径的两个端点,显然可以以权值为下标建立线段树,存储 DFS 序的最大值,欧拉序的最大最小值,这样可以快速确定可能的端点 \(u,v\)

考虑检验这条路径是否合法,首先是路径上的边数 \(dis(u,v)=r-l\),这个用 LCA 可以快速计算

其次还需要满足路径上所有点权值都在 \([l,r]\) 内,树剖后用线段树维护区间最大/最小值,判断是否超出范围

总复杂度 \(O(n\log^2 n)\)

#include<cstdio>
#include<iostream>
#include<set>
#include<vector>
#include<algorithm>
#include<utility>
#define RI register int
#define CI const int&
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
const int N=200005;
int t,n,q,a[N],pos[N],seq[N],dfn[N],dfnr[N],opt,x,y; vector <int> v[N];
#define TN CI now=1,CI l=1,CI r=n
#define LS now<<1,l,mid
#define RS now<<1|1,mid+1,r
class Segment_Tree1
{
	private:
		pi mxdfn[N<<2],mndfnr[N<<2],mxdfnr[N<<2];
        inline void pushup(CI now)
        {
            mxdfn[now]=max(mxdfn[now<<1],mxdfn[now<<1|1]);
            mndfnr[now]=min(mndfnr[now<<1],mndfnr[now<<1|1]);
            mxdfnr[now]=max(mxdfnr[now<<1],mxdfnr[now<<1|1]);
        }
	public:
		inline void build(TN)
		{
			if (l==r)
            {
                int x=pos[l]; mxdfn[now]={dfn[x],x};
                mndfnr[now]=mxdfnr[now]={dfnr[x],x};
                return;
            }
			int mid=l+r>>1; build(LS); build(RS); pushup(now);
		}
		inline void update(CI pos,CI ndfn,CI ndfnr,CI num,TN)
		{
			if (l==r)
            {
                mndfnr[now]=mxdfnr[now]={ndfnr,num};
                mxdfn[now]={ndfn,num}; return;
            }
            int mid=l+r>>1;
			if (pos<=mid) update(pos,ndfn,ndfnr,num,LS); else update(pos,ndfn,ndfnr,num,RS);
            pushup(now);
		}
		inline pi query_maxdfn(CI beg,CI end,TN)
		{
			if (beg<=l&&r<=end) return mxdfn[now]; int mid=l+r>>1;
            if (end<=mid) return query_maxdfn(beg,end,LS);
            if (beg>mid) return query_maxdfn(beg,end,RS);
            return max(query_maxdfn(beg,end,LS),query_maxdfn(beg,end,RS));
		}
		inline pi query_mindfnr(CI beg,CI end,TN)
		{
			if (beg<=l&&r<=end) return mndfnr[now]; int mid=l+r>>1;
            if (end<=mid) return query_mindfnr(beg,end,LS);
            if (beg>mid) return query_mindfnr(beg,end,RS);
            return min(query_mindfnr(beg,end,LS),query_mindfnr(beg,end,RS));
		}
		inline pi query_maxdfnr(CI beg,CI end,TN)
		{
			if (beg<=l&&r<=end) return mxdfnr[now]; int mid=l+r>>1;
            if (end<=mid) return query_maxdfnr(beg,end,LS);
            if (beg>mid) return query_maxdfnr(beg,end,RS);
            return max(query_maxdfnr(beg,end,LS),query_maxdfnr(beg,end,RS));
		}
}SEG1;
class Segment_Tree2
{
	private:
		int mn[N<<2],mx[N<<2];
        inline void pushup(CI now)
        {
            mn[now]=min(mn[now<<1],mn[now<<1|1]);
            mx[now]=max(mx[now<<1],mx[now<<1|1]);
        }
	public:
		inline void build(TN)
		{
			if (l==r) return (void)(mn[now]=mx[now]=a[seq[l]]);
			int mid=l+r>>1; build(LS); build(RS); pushup(now);
		}
		inline void update(CI pos,CI mv,TN)
		{
			if (l==r) return (void)(mn[now]=mx[now]=mv); int mid=l+r>>1;
			if (pos<=mid) update(pos,mv,LS); else update(pos,mv,RS);
            pushup(now);
		}
		inline int query_min(CI beg,CI end,TN)
		{
			if (beg<=l&&r<=end) return mn[now]; int mid=l+r>>1;
            if (end<=mid) return query_min(beg,end,LS);
            if (beg>mid) return query_min(beg,end,RS);
            return min(query_min(beg,end,LS),query_min(beg,end,RS));
		}
		inline int query_max(CI beg,CI end,TN)
		{
			if (beg<=l&&r<=end) return mx[now]; int mid=l+r>>1;
            if (end<=mid) return query_max(beg,end,LS);
            if (beg>mid) return query_max(beg,end,RS);
            return max(query_max(beg,end,LS),query_max(beg,end,RS));
		}
}SEG2;
#undef TN
#undef LS
#undef RS
namespace Heavy_Division
{
    int idx,idxr,top[N],dep[N],sz[N],anc[N],son[N];
    inline void DFS1(CI now=1,CI fa=0)
    {
        son[now]=0; sz[now]=1; dep[now]=dep[fa]+1; anc[now]=fa;
        for (int to:v[now]) if (to!=fa)
        {
            DFS1(to,now); sz[now]+=sz[to];
            if (sz[to]>sz[son[now]]) son[now]=to;
        }
    }
    inline void DFS2(CI now=1,CI tf=1)
    {
        seq[dfn[now]=++idx]=now; top[now]=tf;
        if (son[now]) DFS2(son[now],tf);
        for (int to:v[now]) if (to!=anc[now]&&to!=son[now]) DFS2(to,to);
        dfnr[now]=++idxr;
    }
    inline bool query_path(int x,int y,CI L,CI R)
    {
    	int ret=0;
    	while (top[x]!=top[y])
    	{
    		if (dep[top[x]]<dep[top[y]]) swap(x,y);
    		if (SEG2.query_min(dfn[top[x]],dfn[x])<L) return 0;
            if (SEG2.query_max(dfn[top[x]],dfn[x])>R) return 0;
    		x=anc[top[x]];
    	}
    	if (dep[x]<dep[y]) swap(x,y);
        if (SEG2.query_min(dfn[y],dfn[x])<L) return 0;
        if (SEG2.query_max(dfn[y],dfn[x])>R) return 0;
		return 1;
    }
    inline int LCA(int x,int y)
    {
    	while (top[x]!=top[y])
    	{
    		if (dep[top[x]]<dep[top[y]]) swap(x,y);
    		x=anc[top[x]];
    	}
    	if (dep[x]<dep[y]) swap(x,y);
		return y;
    }
    inline int getdis(CI x,CI y)
    {
        return dep[x]+dep[y]-2*dep[LCA(x,y)];
    }
};
using namespace Heavy_Division;
int main()
{
    int size(256<<20); //256M
    __asm__ ( "movq %0, %%rsp\n"::"r"((char*)malloc(size)+size));
    ios::sync_with_stdio(0); cin.tie(0);
	for (cin>>t;t;--t)
	{
		RI i; for (cin>>n,i=1;i<=n;++i) cin>>a[i],pos[a[i]]=i;
		for (i=1;i<n;++i) cin>>x>>y,v[x].push_back(y),v[y].push_back(x);
		DFS1(); DFS2(); SEG1.build(); SEG2.build();
		for (cin>>q;q;--q)
		{
            cin>>opt>>x>>y;
            if (opt==1)
            {
                int u=a[x],v=a[y];
                SEG1.update(a[x],dfn[y],dfnr[y],y);
                SEG1.update(a[y],dfn[x],dfnr[x],x);
                SEG2.update(dfn[x],a[y]);
                SEG2.update(dfn[y],a[x]);
                swap(a[x],a[y]); swap(pos[u],pos[v]);
            } else
            {
                int u=SEG1.query_maxdfn(x,y).se,v=SEG1.query_mindfnr(x,y).se;
                if (u==v) v=SEG1.query_maxdfnr(x,y).se;
                cout<<(getdis(u,v)==y-x&&query_path(u,v,x,y)?"Yes\n":"No\n");
            }
		}
		for (idx=idxr=0,i=1;i<=n;++i) v[i].clear();
	}
    exit(0);
	return 0;
}

黑洞合并

祁神手玩了半天发现最后的贡献好像和操作顺序无关,因此只要简单的模拟即可

具体原理我也不清楚,总之牛逼就对了

#include<bits/stdc++.h>
using namespace std;
#define int long long

const int MOD = 998244353;
void add(int &x, int a){if ((x+=a)>=MOD) x-=MOD;}

void solve(){
    int n; cin >> n;
    vector<int> vec(n);
    for (int i=0; i<n; ++i) cin >> vec[i];
    int ans=0;
    for (int j=n-1; j>0; --j){
        add(ans, vec[0]*vec[j]%MOD*((vec[0]+vec[j])%MOD)%MOD);
        add(vec[0], vec[j]);
    }
    cout << ans << '\n';
}

signed main(){
    ios::sync_with_stdio(0); cin.tie(0);
    int t; cin >> t; while (t--) solve();
    return 0;
}

亡语

总感觉是我没搞懂题目要干嘛,写完一直 WA 也不知道咋回事,等数据和题解出来再看下吧


怪物猎人

算出可能进行的轮数的上下界,如果这个界内含有两个及以上的整数则两个人都有解,否则按奇偶性输出一个人即可

#include<cstdio>
#include<iostream>
#define int long long
#define RI register int
#define CI const int&
using namespace std;
int t,k,x,y;
signed main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    for (cin>>t;t;--t)
    {
        cin>>k>>x>>y;
        int L=(k+x-1)/x,R=(k+y-1)/y;
        if (L!=R) cout<<"Yes\nYes\n"; else
        {
            if (L%2==1) cout<<"Yes\nNo\n";
            else cout<<"No\nYes\n";
        }
    }
    return 0;
}

融合矿石

跑两遍完全背包,先求出 \(f_x\) 表示体积为 \(x\) 的石头最多能有多少“金辉石”,然后就能顺便算出对应的贡献

再求出 \(g_x\) 表示体积为 \(x\) 的石头最大总价值,总复杂度 \(O(nm)\)

#include<bits/stdc++.h>
using namespace std;
// #define int long long

using pii = pair<int, int>;
#define ft first
#define sd second

const int N = 3005;
const int INF = (int)1e9+5;
int f[N], g[N];

void solve(){
    vector<int> v(10);
    for (int i=0; i<10; ++i) cin >> v[i];
    int n, m; cin >> n >> m;
    vector<pii> vec(n+1);
    for (int i=1; i<=n; ++i){
        int a, b; cin >> a >> b;
        vec[i] = {a, b};
    }

    f[0] = 0;
    for (int j=1; j<=m; ++j) f[j] = -INF;
    for (int i=1; i<=n; ++i){
        for (int j=vec[i].ft; j<=m; ++j){
            f[j] = max(f[j], f[j-vec[i].ft]+vec[i].sd);
        }
    }

    // printf("f:"); for (int j=0; j<=m; ++j) printf("%d ", f[j]); puts("");

    auto calc = [&](int a, int b){
        for (int i=9; i>=0; --i){
            if (10*b > a*i) return v[i];
        }
        return 0;
    };

    g[0] = 0;
    for (int j=1; j<=m; ++j) g[j] = -INF;
    for (int i=1; i<=m; ++i){
        for (int j=i; j<=m; ++j){
            g[j] = max(g[j], g[j-i]+i*calc(i, f[i]));
        }
    }
    // printf("g:"); for (int j=0; j<=m; ++j) printf("%d ", g[j]); puts("");

    cout << g[m] << '\n';
}

signed main(){
    ios::sync_with_stdio(0); cin.tie(0);
    int t; cin >> t; while (t--) solve();
    return 0;
}

小猫钓鱼

简单手玩发现两个人初始时的 pair 数是相同的,不妨记为 \(c\)

手玩一下发现当且仅当 \(c=0\) 时后手胜,否则总是先手胜

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,a[N],b[N],c[N];
int main()
{
    ios::sync_with_stdio(0); cin.tie(0);
    for (cin>>t;t;--t)
    {
        cin>>n; int cnt=0;
        for (RI i=1;i<=n;++i) cin>>a[i];
        for (RI i=1;i<=n;++i) cin>>b[i];
        for (RI i=1;i<=n;++i) c[i]=0;
        for (RI i=1;i<=n;++i) if (++c[a[i]]==2) ++cnt;
        // if (cnt>=2) puts("Tie"); else
        // if (cnt==1) puts("shuishui"); else
        // puts("sha7dow");
        puts(cnt>0?"shuishui":"sha7dow");
    }
    return 0;
}

长期素食

比赛的时候以为只要求出每天的最大/次大直线即可 DP 求解,后面得知好像要求出前三大

这类找次大/次次大最优直线的问题可以用随机化分组转化为求最大直线的经典问题从而求凸壳求解,但祁神赛后才把赛时的求前二大改为求前三大,现在数据没出也测不了


收集签名

徐神赛时写了 DP 的做法但一直 WA,赛后发现有特判写错了,也是因为还没数据测不了先坑着


Postscript

由于今天要整理东西准备明天出发北京了,因此这场以及之前欠下的一些博客都留着回来后再补吧

标签:钉耙,CI,return,int,编程,2024,query,end,now
From: https://www.cnblogs.com/cjjsb/p/18363504

相关文章

  • 嵌入式学习 20(Linux高级编程——文件——misc)
     文件操作相关函数一、symlink函数intsymlink(constchar*oldpath,constchar*newpath);功能:创建一个指向oldpath文件的新的符号链接(软链接)文件。参数:•oldpath:被链接指向的原始文件的路径。•newpath:新创建的符号链接文件的路径。返回值:•成功时,返回0。......
  • Python教程(十五):IO 编程
    目录专栏列表引言基础概念什么是IO?同步IOvs异步IO同步IO(SynchronousIO)异步IO(AsynchronousIO)Python中的IO标准IO标准输入和输出文件IO文件操作的上下文管理器打开文件读取文件操作内存中的数据高级文件操作读写二进制文件使用文件指针网络IO使用`requests`库使用......
  • 稀里糊涂赚了 100 多! 2024年美团圈圈还可以玩玩
    开通美团圈圈达人一个多月了,稀里糊涂赚到100多......
  • 2024杭电多校第十场 1002树上询问(题解)
    题意给一棵树,每个节点有一个权值,并且权值是一个排列。接下来有多次操作,每次操作要么是交换两个节点权值,要么是询问一个权值区间\([L,R]\),判断是否存在树上的一个路径,使得路径上的权值恰好都在这个区间里分析由于询问的是树上的一个路径,联想到了树上莫队中对路径的处理。这里......
  • KubeSphere 社区双周报| 2024.08.02-08.15
    KubeSphere社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过commit的贡献者,并对近期重要的PR进行解析,同时还包含了线上/线下活动和布道推广等一系列社区动态。本次双周报涵盖时间为:2024.08.02-08.15。贡献者名单新晋KubeSpherecontribu......
  • 2024.8.16(ansible)
    一、回顾1、mysql和python    1.mysql5.7        1.1不需要执行mysql_ssl_rsa_setup        1.2Change_master_to.不需要getpublickey    2.可以使用pymysql非交互的管理mysql        2.1 conn......
  • 2024.8.15(python管理mysql、Mycat实现读写分离)
    一、python管理mysql1、搭建主mysql[root@mysql57~]#tar-xfmysql-5.7.44-linux-glibc2.12-x86_64.tar.gz [root@mysql57~]#cp-rmysql-5.7.44-linux-glibc2.12-x86_64/usr/local/mysql[root@mysql57~]#rm-rf/etc/my.cnf[root@mysql57~]#mkdir/usr/local/......
  • 智能Java开发工具IntelliJ IDEA v2024.2全新发布——更好支持Spring开发
    IntelliJIDEA,是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的java开发工具,尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、创新的GUI设计等方面的功能可以说是超常的。立即获取IntelliJIDEAv202......
  • 20240326 windows搭建k8s环境
    windows搭建k8s环境安装docker-desktop在界面中找到/设置/Resources/Advanced/Diskimagelocation,选择一个非C盘的目录利用minikube安装已经安装玩docker-desktop或者virtualbox参考文档minikube官方文档https://www.cnblogs.com/yumingkuan/p/16750618.htmlhttps://......
  • 20240110 windows安装make工具
    从https://sourceforge.net/projects/mingw/下载文件并安装安装后打开MinGW,依次选择如下3个红框的包,右键“Markforinstallation”勾选需要安装的包后,执行“installation/ApplyChanges”将c:\MinGW\bin\ming32-make.exe重命名为c:\MinGW\bin\make.exe将MinGW的......