首页 > 其他分享 ># 2023省选武汉联测12

# 2023省选武汉联测12

时间:2023-04-20 21:12:30浏览次数:42  
标签:12 const 省选 sum max1 int 联测 now Data

T1 图案

首先是题解做法:考虑对于每个 \(r\) ,判断 \(s[1,r]\) 是否为一个图案,设 \(r=ik+j\) ,其中 \(0\le j\le i\) ,如果存在一组这样的 \((i,j)\) 满足 \(s[1,r-i]=s[i+1,r]\) ,那么 \(s[1,r]\) 是一个图案,考虑这样做的正确性,如果 \(s[1,r-i]=s[i+1,r]\) ,那么一定有 \(s[i+1,r-2i]=s[2i+1,r-i]\) ,这样操作 \(k\) 次,剩余串的长度一定为 \(j\) ,容易判断这个串是一个图案。

因此我们枚举 \(i\) ,发现合法的 \(r\) 的区间为 \([ki,(k+1)i]\cap[1,\operatorname{lcp}(s[1,n],s[i+1,n])]\) ,求解过程可以用二分哈希,用 Z 函数可以做到 \(O(n)\) 。

简单叙述一下自己的想法,仍然考虑枚举 \(BA\) 的长度 \(len\) ,此时需要判断前缀由 \(k\) 个长度为 \(len\) 的相同的字符串拼接而成,发现暴力枚举判断的复杂度为 \(o(n)\) ,因此直接暴力即可,之后找到 \(s[klen+1,n]\) 和 \(s[1,n]\) 的 \(\operatorname{lcp}\) 做区间覆盖即可。

code

#include <cstdio>
#include <algorithm>
using namespace std;
const int max1 = 1e6;
const int base = 29, mod1 = 998244353, mod2 = 20051107;
int n, k;
char s[max1 + 5];
int f[max1 + 5];
struct Data
{
	int h1, h2;
	Data () {}
	Data ( int __h1, int __h2 )
		{ h1 = __h1, h2 = __h2; }
	Data operator + ( const Data &A ) const
		{ return Data(( h1 + A.h1 ) % mod1, ( h2 + A.h2 ) % mod2); }
	Data operator - ( const Data &A ) const
		{ return Data(( h1 - A.h1 + mod1 ) % mod1, ( h2 - A.h2 + mod2 ) % mod2); }
	Data operator * ( const Data &A ) const
		{ return Data(1LL * h1 * A.h1 % mod1, 1LL * h2 * A.h2 % mod2); }
	bool operator == ( const Data &A ) const
		{ return h1 == A.h1 && h2 == A.h2; }
	bool operator != ( const Data &A ) const
		{ return h1 != A.h1 || h2 != A.h2; }
};
struct Hash
{
	Data power[max1 + 5], h[max1 + 5];
	void Build ( const char *s, const int &len )
	{
		power[0] = Data(1, 1);
		for ( int i = 1; i <= len; i ++ )
			power[i] = power[i - 1] * Data(base, base);
		h[0] = Data(0, 0);
		for ( int i = 1; i <= len; i ++ )
			h[i] = h[i - 1] * Data(base, base) + Data(s[i] - 'a' + 1, s[i] - 'a' + 1);
		return;
	}
	Data Query ( int L, int R )
		{ return h[R] - h[L - 1] * power[R - L + 1]; }
}H;
int main ()
{
	freopen("pattern.in", "r", stdin);
	freopen("pattern.out", "w", stdout);
	scanf("%d%d%s", &n, &k, s + 1);
	H.Build(s, n);
	for ( int len = 1; len <= n; len ++ )
	{
		if ( k * len > n )
			break;
		bool flag = true;
		Data d = H.Query(1, len);
		for ( int i = 2; i <= k; i ++ )
			if ( H.Query(i * len - len + 1, i * len) != d )
				{ flag = false; break; }
		if ( flag )
		{
			int L = 0, R = min(n - k * len, len), pos = 0;
			while ( L <= R )
			{
				int mid = L + R >> 1;
				if ( H.Query(1, mid) == H.Query(k * len + 1, k * len + mid) )
					pos = mid, L = mid + 1;
				else
					R = mid - 1;
			}
			++f[k * len];
			--f[k * len + R + 1];
		}
	}
	for ( int i = 1; i <= n; i ++ )
	{
		f[i] += f[i - 1];
		if ( f[i] )
			putchar('1');
		else
			putchar('0');
	}
	putchar('\n');
	return 0;
}

T2 树点购买

首先有一个显然的结论:任意一颗子树内最多有一个密码未知的叶子。考虑利用这个性质进行 dp ,设 \(f_{u,0/1}\) 表示当前考虑以 \(u\) 为根的子树,子树内存在 \(0/1\) 个密码未知的叶子,比较显然转移有:

\[\begin{aligned} f_{u,0}&=\min(\sum_{v}f_{v,0},\sum_{v}f_{v,0}-\max_{v}(f_{v,0}-f_{v,1})+c_{u})\\ f_{u,1}&=\sum_{v}f_{v,0}-\max_{v}(f_{v,0}-f_{v,1}) \end{aligned} \]

通过 \(f\) 的值由哪些状态转移过来,解决剩余两个问题。

首先证明一个结论: \(f_{u,0}\) 对应的所有方案中被选择节点构成的集合一定是 \(f_{v,1}\) 对应的所有方案中被选择节点构成集合的超集。

考虑归纳证明:

当 \(u\) 为叶节点时, \(f_{u,0}\) 对应 \(\{u\}\) , \(f_{u,1}\) 对应 \(\emptyset\) ,显然成立。

当 \(u\) 不为叶节点时,如果 \(\max(f_{v,0}-f_{v,1})\) 在一个儿子 \(w\) 中取到,那么 \(f_{u,0}\) 对应的集合为 \(f_{v,0}\) 对应集合的并集或者 \(\{u\}\) 与 \(f_{v,0},(v\ne w)\) 对应集合与 \(f_{w,1}\) 对应集合的并集, \(f_{v,1}\) 对应集合为 \(f_{v,0},(v\ne w)\) 对应集合与 \(f_{w,1}\) 对应集合的并集;如果 \(\max(f_{v,0}-f_{v,1})\) 在多个儿子中被取到,那么 \(f_{u,0}\) 对应集合为 \(f_{v,0}\) 对应集合的并集或 \(\{u\}\) 与 \(f_{v,0}\) 对应集合的并集, \(f_{u,1}\) 对应集合为 \(f_{v,0}\) 对应集合的并集。显然上述两种情况均满足 \(f_{u,0}\) 对应集合为 \(f_{u,1}\) 对应集合的超集。

因此考虑再次 Dfs ,定义函数 \(\operatorname{Redfs}(u,type)\) 可以找到 \(f_{u,type}\) 对应的集合,根据上述结论,如果存在两个方案满足一个包含 \(f_{v,0}\) ,一个包含 \(f_{v,1}\) ,只需要调用 \(\operatorname{Redfs}(v,1)\) 即可,可以保证复杂度。

考虑解决第三个问题,定义 \(g_{u,0/1}\) 为 \(f_{u,0/1}\) 对应的方案数,按照转移到 \(f_{u,0/1}\) 的状态计算 \(g_{u,0/1}\) 即可,比较简单,在此不再赘述。

code

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int mod = 998244353;
const int max1 = 1e6;
const long long inf = 0x3f3f3f3f3f3f3f3f;
int n, k, c[max1 + 5];
vector <int> edge[max1 + 5];
int son[max1 + 5];
long long f[max1 + 5][2], sum[max1 + 5], Max[max1 + 5];
int cnt[max1 + 5], pi[max1 + 5], g[max1 + 5][2];
bool choose[max1 + 5];
void Dfs ( int now, int fa )
{
    son[now] = 0;
    sum[now] = 0;
    Max[now] = -inf, cnt[now] = 0;
    pi[now] = 1;
    for ( auto v : edge[now] )
    {
        if ( v == fa )
            continue;
        ++son[now];
        Dfs(v, now);
        sum[now] += f[v][0];
        if ( Max[now] < f[v][0] - f[v][1] )
        {
            Max[now] = f[v][0] - f[v][1];
            cnt[now] = 0;
        }
        if ( Max[now] == f[v][0] - f[v][1] )
            ++cnt[now];
        pi[now] = 1LL * pi[now] * g[v][0] % mod;
    }
    if ( !son[now] )
    {
        f[now][0] = c[now];
        f[now][1] = 0;
        g[now][0] = g[now][1] = 1;
    }
    else
    {
        int p = 0, q = 1;
        for ( auto v : edge[now] )
        {
            if ( v == fa )
                continue;
            p = 1LL * p * g[v][0] % mod;
            if ( f[v][0] - f[v][1] == Max[now] )
                p = ( p + 1LL * q * g[v][1] ) % mod;
            q = 1LL * q * g[v][0] % mod;
        }
        f[now][0] = min(sum[now], sum[now] - Max[now] + c[now]);
        g[now][0] = 0;
        if ( sum[now] <= sum[now] - Max[now] + c[now] )
            g[now][0] = ( g[now][0] + pi[now] ) % mod;
        if ( sum[now] >= sum[now] - Max[now] + c[now] )
            g[now][0] = ( g[now][0] + p ) % mod;
        f[now][1] = sum[now] - Max[now];
        g[now][1] = p;
    }
    return;
}
void Redfs ( int now, int fa, int type )
{
    if ( !type )
    {
        if ( !son[now] )
            choose[now] = true;
        else
        {
            if ( sum[now] < sum[now] - Max[now] + c[now] )
            {
                for ( auto v : edge[now] )
                {
                    if ( v == fa )
                        continue;
                    Redfs(v, now, 0);
                }
            }
            else if ( sum[now] > sum[now] - Max[now] + c[now] )
            {
                choose[now] = true;
                for ( auto v : edge[now] )
                {
                    if ( v == fa )
                        continue;
                    if ( f[v][0] - f[v][1] == Max[now] )
                        Redfs(v, now, cnt[now] == 1);
                    else
                        Redfs(v, now, 0);
                }
            }
            else
            {
                choose[now] = true;
                for ( auto v : edge[now] )
                {
                    if ( v == fa )
                        continue;
                    Redfs(v, now, 0);
                }
            }
        }
    }
    else
    {
        for ( auto v : edge[now] )
        {
            if ( v == fa )
                continue;
            if ( f[v][0] - f[v][1] == Max[now] )
                Redfs(v, now, cnt[now] == 1);
            else
                Redfs(v, now, 0);
        }
    }
}
int main ()
{
    freopen("purtree.in", "r", stdin);
    freopen("purtree.out", "w", stdout);
    scanf("%d", &n);
    for ( int i = 1; i <= n; i ++ )
        scanf("%d", &c[i]);
    for ( int i = 1, u, v; i <= n - 1; i ++ )
        scanf("%d%d", &u, &v), edge[u].push_back(v), edge[v].push_back(u);
    scanf("%d", &k);
    Dfs(1, 0);
    Redfs(1, 0, 0);
    printf("%lld\n", f[1][0]);
    if ( k > 1 )
    {
        for ( int i = 1; i <= n; i ++ )
            if ( choose[i] )
                printf("%d ", i);
        printf("\n");
    }
    if ( k > 2 )
        printf("%d\n", g[1][0]);
    return 0;
}

T3 舰队游戏

由于原图构成 DAG ,因此设 \(f_{u,i}\) 表示当前位于第 \(u\) 个节点,剩余生命值为 \(i\) 时的期望步数,显然有转移:

\[f_{u,i}=min(1+\tfrac{1}{deg}\sum_{v}f_{v,i-d_v},f_{1,H}+H-i) \]

发现转移存在后效性,但是这只涉及了 \(f_{1,H}\) ,因此考虑确定 \(f_{1,H}\) 的值进行转移,具体的,我们二分 \(f_{1,H}\) 的值为 \(x\) 进行 dp ,如果 dp 得到的 \(f_{1,H}\) 等于二分的值,那么 \(L=mid\) ,否则 \(R=mid\) 。

考虑这样做的正确性,将 \(f_{u,i}\) 看做关于 \(x\) 的函数,用归纳法很容易证明 \(f_{u,i}(x)\) 的导数 \(\le 1\) ,那么 \((x-f_{u,i})'\ge 0\) ,因此函数具有单调性,可以二分求解。

code

#include <cstdio>
#include <vector>
#include <cmath>
#include <cassert>
using namespace std;
const int max1 = 100;
const double inf = 0x3f3f3f3f3f3f3f3f, eps = 1e-8;
int n, m, H, d[max1 + 5];
vector <int> edge[max1 + 5];
double f[max1 + 5][max1 + 5];
double F ( int u, int i )
{
    if ( i <= 0 )
        return inf;
    return f[u][i];
}
bool Check ( double x )
{
    for ( int i = 1; i <= n; i ++ )
        for ( int j = 1; j <= H; j ++ )
            f[i][j] = inf;
    for ( int i = 1; i <= H; i ++ )
        f[n][i] = 0;
    for ( int i = 1; i <= H; i ++ )
    {
        for ( int u = n - 1; u >= 1; u -- )
        {
            int deg = edge[u].size();
            if ( deg )
            {
                f[u][i] = 0;
                for ( auto v : edge[u] )
                    f[u][i] += F(v, i - d[v]);
                f[u][i] = f[u][i] / deg + 1;
            }
            f[u][i] = min(f[u][i], x + H - i);
        }
    }
    return abs(f[1][H] - x) > eps;
}
int main ()
{
    freopen("kancolle.in", "r", stdin);
    freopen("kancolle.out", "w", stdout);
    scanf("%d%d%d", &n, &m, &H);
    for ( int i = 1, u, v; i <= m; i ++ )
    {
        scanf("%d%d", &u, &v);
        edge[u].push_back(v);
    }
    for ( int i = 1; i <= n; i ++ )
        scanf("%d", &d[i]);
    double l = 0, r = 1e6 + 5, ans = -1;
    while ( r - l > eps )
    {
        double mid = 0.5 * ( l + r );
        if ( Check(mid) )
            ans = mid, r = mid;
        else
            l = mid;
    }
    if ( ans < 0 )
        printf("-1\n");
    else
        printf("%.8lf\n", ans);
    return 0;
}

标签:12,const,省选,sum,max1,int,联测,now,Data
From: https://www.cnblogs.com/KafuuChinocpp/p/17338347.html

相关文章

  • 2023省选武汉联测11
    T1游戏对于树上三点\((u,v,w)\),一定存在一个点\(p\)满足\(p\tou\)与\(p\tov\)与\(p\tow\)的路径两两不重合,考虑枚举\(p\)计算答案,由于题目给定\(\operatorname{dis}(u,v),\operatorname{dis}(u,w),\operatorname{dis}(v,w)\),因此我们首先用解方程的方法求解\(......
  • 12-CSS3属性详解:动画详解
    title:12-CSS3属性详解:动画详解publish:true前言本文主要内容:过渡:transition2D转换transform3D转换transform动画:animation过渡:transitiontransition的中文含义是过渡。过渡是CSS3中具有颠覆性的一个特征,可以实现元素不同状态间的平滑过渡(补间动画),经常......
  • 12-HTML基础回顾
    title:12-HTML基础回顾本文主要内容html的常见元素html元素的分类html元素的嵌套关系html元素的默认样式和CSSResethtml常见面试题html的常见元素html的常见元素主要分为两类:head区域的元素、body区域的元素。下面来分别介绍。1、head区域的......
  • vue3微信公众号商城项目实战系列(12)项目发布到服务器上
    本篇介绍如何将vue3项目打包发布到服务器上,然后在微信公众号上打开。vue3发布之前需要对项目进行编译,编译时会在项目根目录下创建dist文件夹,编译后的文件会存放在这里。 在编译之前,我们在public目录下建一个config.js的文件,里面放如下的代码:constconfig={baseUr......
  • 最少拦截系统 1257 (动态规划)
    最少拦截系统TimeLimit:2000/1000MS(Java/Others)   MemoryLimit:65536/32768K(Java/Others)TotalSubmission(s):27022   AcceptedSubmission(s):10667ProblemDescription某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系......
  • Python 图像处理实用指南:11~12
    原文:Hands-OnImageProcessingwithPython协议:CCBY-NC-SA4.0译者:飞龙本文来自【ApacheCN计算机视觉译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。当别人说你没有底线的时候,你最好真的没有;当别人说你做过某些事的时候,你也最好真的做过。十一、深入学习图像处理——......
  • Oracle MySQL Server 拒绝服务漏洞(CVE-2023-21912) 修复
    CVE编号公告标题和摘要最高严重等级受影响的软件CVE-2023-21912OracleMySQLServer拒绝服务漏洞未经身份验证的远程攻击者可通过MySQL协议网络访问MySQLServer,成功利用此漏洞可导致目标MySQLServer挂起或频繁重复崩溃,造成拒绝服务攻击重要MySQLServer<=5.7.41......
  • CDGA敏捷开发的12个原则在企业数据治理中的应用
    敏捷数据治理,就是用敏捷的核心思想来管理数据,实现数据的持续、高质量交付,以应对业务的不断变化。敏捷开发的12个原则如下:原则1:尽早并持续的交付有价值的软件以满足客户需求。对数据治理来讲,如何让业务快速拿到想要的数据是需要重点考虑的问题。业务人员应当参与数据需求分析,甚至取......
  • Solon v2.2.12 发布,Java 应用开发框架
    Solon是一个高效的Java应用开发框架:更快、更小、更简单。它不是Spring、没有使用Servlet、JavaEE接口,是一个有自己接口标准的开放生态:150多个生态插件,可以满足各种场景开发大量的国产框架适配,可以为应用软件国产化提供更好支持,助力信创建设相对于SpringBoot和Sprin......
  • 一招解决由于找不到msvcp120.dll,无法继续执行代码的方法
    msvcp120.dll是vs2010编译的程序默认的库文件。msvcp120.dll可以解决电脑软件或某些大型游戏、程序由于vs2010编译系统中缺失此dll的问题。vs2010编写的程序运行所需dll。下载msvcp120.dll文件打开电脑随便一个浏览器顶部网页输入 【 dll修复程序.site 】进入后点击开始下载dl......