首页 > 其他分享 >2024.3.30 模拟赛

2024.3.30 模拟赛

时间:2024-04-04 18:45:33浏览次数:27  
标签:2024.3 int 30 fa en ans include find 模拟

A 数列删除

至少删除 \(m\) 个数,意思就是最多保留 \(n-m\) 个数。

删除的总和最小,意思就是保留的总和最大。

非降子序列问题可以用经典的动态规划来解决。

用 \(f[i][j]\) 表示,当前选的最后一个数是 \(a[i]\),一共选了 \(j\) 个数,选的数总和最大是多少。

转移就是枚举上一个数 \(a[k]\),满足 \(k<i\&\&a[k]\leq a[i]\),\(f[i][j]\) 可以用 \(f[k][j-1]+a[i]\) 转移。

#include<bits/stdc++.h>

using namespace std;

int f[1010][1010];
int a[1010];
int main() {
    int n, m, i, j, k, sum = 0, ans = 0;
    cin >> n >> m;
    for ( i = 1 ; i <= n ; i++)
        cin >> a[i], sum += a[i];
    
    for ( i = 1; i <= n; i++ ) {
        f[i][1] = a[i];
        for (j = 2; j <= n-m; j++)
            for (k = 1; k < i; k++)
                if (a[k] <= a[i])
                    f[i][j] = max(f[i][j], f[k][j-1]+a[i]);
        
        for (j = 1; j <= n-m; j++)
            ans=max(ans, f[i][j]);
    }

    cout<<sum-ans;
    

    return 0;
}

B 游戏升级

对应题目数据范围:

\(10pts\):输出 \(0\) 即可。

\(20pts\):暴力即可。

\(20pts\):\(x>A_1\) 时小明无法升级,暴力枚举小于等于 \(A_1\) 的 \(x\),大于 \(A_1\) 的整体处理。

\(20pts\):即 \(\lfloor \frac {A_1}x\rfloor=\lfloor \frac {A_2}x\rfloor\)。乱设的部分分,其实是提醒你考虑 \(\lfloor \frac {A_1}x\rfloor\) 的取值种类。

对于 \(100\%\) 的数据:只需要发现 \(\lfloor \frac {A_i}x\rfloor\) 的取值种类只有 \(O(\sqrt{A_i})\) 种。然后这题就没了,可以用类似整除分块的写法求出有多少组这样的取值。复杂度 \(O(T\sqrt{A})\)。

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

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int T;
    cin >> T;
    while (T--) {
        int a1, b1, a2, b2, n;
        cin >> a1 >> b1 >> a2 >> b2 >> n;
        int ans = 0;
        for (int l = 1, r, i; l <= a1 + 1; l = r + 1) {
            i = a1 / l;
            r = i ? a1 / i : 1e9;
            int j = i + b1 - b2;
            if (j < 0 || j > a2)
                continue;
            int l2 = a2 / (j + 1) + 1, r2 = j ? a2 / j : 1e9;
            // cerr<<"> "<<i<<" "<<j<<"  "<<l<<" "<<r<<"  "<<l2<<" "<<r2<<endl;
            l2 = max(l, l2);
            r2 = min(r, r2);
            r2 = min(n, r2);
            if (l2 <= r2)
                ans += r2 - l2 + 1;
        }
        cout << ans << '\n';
    }
}

C 难题

测试点 \(1,2\)

暴力枚举初始的 \(x,y\),然后不断进行 \(x=x+y,y=x+y\) 判断是否存在某一时刻 \(x=X\)。

测试点 \(3,4\)

只枚举初始 \(x\) 的取值,设 \(y=k\),然后带入 \(x=x+y,y=x+y\) 的过程中,每个时刻 \(x\) 的值是 \(ak+b\) 的形式,其中 \(a,b\) 是定值,然后就是判断 \(ak+b=X\) 是否有 \(k\in [1,M]\) 的解了。

测试点 \(5,6\)

输出上面的 \(a,b\) 可以发现 \(a=f_i,b=f_{i+1}\),其中 \(i\) 为奇数。

然后就是求 \(xf_i+yf_{i+1}=N(x\in [0,N],y\in [0,M])\) 的解的个数,因为 \(gcd(f_i,f_{i+1})=1\),可以用扩欧求一个特解 \(x_0,y_0\)。满足条件的 \(x\equiv x_0~mod~f_{i+1},y\equiv y_0~mod~f_i\),因此可以找到 \(x\) 最大且满足条件的解 \((x_1,y_1)\),和 \(x\) 最小且满足条件的解 \((x_2,y_2)\),于是可以算出当前情况下,解的个数为 \(\frac{x_1-x_2}{f_{i+1}}\)。复杂度为 \(O(nlog^2X)\)。

测试点 \(7-10\)

输出每一组 \(x_0,y_0\),会发现 \(x_0=-f_{i-1},y_0=f_{i-2}\)。可用归纳法证明:

假设 \(-f_if_{i-1}+f_{i+1}f_{i-2}=1\) 成立,那么

\[-f_{i+1}f_i+f_{i+2}f_{i-1}\\ =-f_{i+1}(f_{i-1}+f_{i-2})+(f_{i+1}+f_i)f_{i-1}\\ =-f_{i+1}f_{i-2}+f_if_{i-1}=-1 \]

所以 \(-f_{i+2}f_{i+1}+f_{i+3}f_i=1\)(注意上面枚举的 \(i\) 为奇数),然后就可以优化掉一个 \(log\)。

关于细节

若没开 __\(int128\),或没判断 \(x=0\),或只枚举了 \(70\) 个斐波那契数,会 \(WA\)。

#include <bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x) {
    x = 0;
    short f = 1;
    char c = getchar();
    for (; c < '0' || c > '9'; c = getchar())
        if (c == '-')
            f = -1;
    for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    x *= f;
    return;
}
#define LL long long
#define ll __int128
const int N = 2e5 + 5;
ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) {
        x = 1, y = 0;
        return a;
    }
    ll t = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return t;
}
ll get_inv(ll a, ll p) {
    ll x = 0, y = 0;
    ll t = exgcd(a, p, x, y);
    ll ans = x / t % p;
    return (ans + p) % p;
}
ll inv[N];
int main() {
    int T;
    read(T);
    ll a = 1, b = 1;
    int idx = 0;
    while (1) {
        if (a > 1e18)
            break;
        inv[++idx] = get_inv(a, b);
        a = a + b;
        b = a + b;
    }
    while (T--) {
        ll x, n, m;
        read(x), read(n), read(m);
        ll a = 1, b = 1;
        LL ans = 0;
        int idx = 0;
        if (x == 0) {
            puts("1");
            continue;
        }
        while (1) {
            if (a > x)
                break;
            ll tmp = x % b * inv[++idx] % b;
            ll X = tmp, Y = (x - tmp * a) / b;
            if (Y > m) {
                ll k = (Y - m) / a + ((Y - m) % a != 0);
                Y -= k * a, X += k * b;
                if (Y + a <= m)
                    Y += a, X -= b;
            }
            if (X >= 0 && X <= n && Y >= 0 && Y <= m)
                ans += min(Y / a, (n - X) / b) + 1;
            a = a + b;
            b = a + b;
        }
        printf("%lld\n", ans);
    }
}

D 迷宫逃亡

若知道从安全区 \(i\) 走到安全区 \(j\) 的最短路,并以此为边权连边,容易想到最小生成树,询问时回答路径最大边权。

可以只保留可能出现在最小生成树上的边。注意到如果从 \(x\) 到 \(z\) 的最优路径经过了 \(y\),那么只要连上 \((x,y),(y,z)\),不需要连 \((x,z)\)。因此可以跑一个多源 \(bfs\),计算距离每个湿地最近的是哪个安全区,即每个安全区的管辖范围(是一个连通块)。如果两个连通块不接壤,中间至少间隔一个 \(y\),连边是不优的。若有接壤就连边。

注意到 \(bfs\) 过程中就可以找到所有接壤的地方,即拓展到一个更新过的位置,且所属安全区不同。这样连出来的边数是 \(O(nm)\) 的,总时间复杂度 \(O((nm+p+q)logp)\)。

#include <algorithm>
#include <cstdio>

#define M 2005
#define N 200005

using namespace std;

char a[M][M];
int R,C,en,d[N],f[N],b[M][M],qx[M*M],qy[M*M],px[M*M],py[M*M],ax[N][20],pre[N][20],head[N];
const int dx[4]={1,0,-1,0},
		  dy[4]={0,1,0,-1};
struct edge
{
	int v,w,nxt;
}e[N*2];

int find(int);
bool mrg(int,int);
void dfs(int,int);
void adde(int,int,int);
pair<int,int> lca(int,int);

int main()
{
	int i,k,r,n,q,x,y,u,v,tp,tq,nx,ny;
	scanf("%d %d %d %d",&R,&C,&n,&q);
	for(i=1;i<=R;++i)scanf("%s",a[i]+1);
	for(i=1;i<=n;++i)
	{
		scanf("%d %d",&x,&y);
		f[i]=b[px[i]=x][py[i]=y]=i;
	}
	tp=n;
	for(r=0;tp;++r)
	{
		tq=0;
		for(i=1;i<=tp;++i)
		{
			x=px[i];y=py[i];
			for(k=0;k<4;++k)
				if(b[nx=x+dx[k]][ny=y+dy[k]]&&mrg(b[x][y],b[nx][ny]))
					adde(b[x][y],b[nx][ny],r*2);
		}
		for(i=1;i<=tp;++i)
		{
			x=px[i];y=py[i];
			for(k=0;k<4;++k)
				if(b[nx=x+dx[k]][ny=y+dy[k]])
				{
					if(mrg(b[x][y],b[nx][ny]))
						adde(b[x][y],b[nx][ny],r*2+1);
				}
				else if(nx&&nx<=R&&ny&&ny<=C&&a[nx][ny]=='.')
				{
					b[nx][ny]=b[x][y];
					qx[++tq]=nx;qy[tq]=ny;
				}
		}
		for(i=1;i<=tq;++i)
		{px[i]=qx[i];py[i]=qy[i];}
		tp=tq;
	}
	for(i=1;i<=n;++i)if(!d[i])dfs(i,0);
	while(q--)
	{
		scanf("%d %d",&u,&v);auto k=lca(u,v);
		if(!k.first)puts("-1");
		else printf("%d\n",k.second);
	}
	return 0;
}
void dfs(int u,int fa)
{
	int i,v;
	d[u]=d[pre[u][0]=fa]+1;
	for(i=1;i<=18;++i)
	{
		pre[u][i]=pre[pre[u][i-1]][i-1];
		ax[u][i]=max(ax[u][i-1],ax[pre[u][i-1]][i-1]);
	}
	for(i=head[u];i;i=e[i].nxt)
		if((v=e[i].v)!=fa)
			{ax[v][0]=e[i].w;dfs(v,u);}
}
pair<int,int> lca(int u,int v)
{
	int i,ans=0;
	if(d[u]>d[v])swap(u,v);
	for(i=18;i>=0;--i)
		if(d[u]<=d[v]-(1<<i))
			{ans=max(ans,ax[v][i]);v=pre[v][i];}
	if(u==v)return {u,ans};
	for(i=18;i>=0;--i)
		if(pre[u][i]!=pre[v][i])
		{
			ans=max(ans,max(ax[u][i],ax[v][i]));
			u=pre[u][i];v=pre[v][i];
		}
	ans=max(ans,max(ax[u][0],ax[v][0]));
	return {pre[u][0],ans};
}
int find(int u){return u==f[u]?u:(f[u]=find(f[u]));}
bool mrg(int u,int v){u=find(u);v=find(v);f[v]=u;return u!=v;}
void adde(int u,int v,int w)
{
	e[++en].v=v;e[en].w=w;
	e[en].nxt=head[u];head[u]=en;
	swap(u,v);
	e[++en].v=v;e[en].w=w;
	e[en].nxt=head[u];head[u]=en;
}

E 点格游戏

容易发现连通分量的形状只可能是环或者链,先讨论链的情况:
image
这是一条长度为 \(5\) 的链,若先手操作任意一条边,会形如:
image
此时后手有两种选择:

  1. 填满整条链,获得等同于链长的分数,然后还要再操作一步,相当于删除一条链,交换先后手。

  2. 把链填成只剩两个相邻的格子,获得链长 \(-2\) 的分数,后手画两个格子中间的边,接下来先后手不变。

第二种选择图示如下:
Image
环的情况类似,但如果后手想先后手顺序不变,需要让出四个格子。
image
还有一些特殊情况,例如长度为 \(1/2\) 的链,先手一定会优先从小到大操作这些链,每次操作让后手获得链上的格子,同时交换先后手。

至此流程已经明了:

  1. 先手选择长度为 \(1/2\) 的链,后手获得链上的格子,同时交换先后手。

  2. 先手选择一条链/一个环,后手选择交换先后手/先后手不变,获得相应分数。

容易发现先手选的链/环一定是当前链/环中长度最小的,可以设 \(dp_{i,j}\) 表示当前只剩下最大的 \(i\) 条链,\(j\) 个环,此时游戏分数的最大值是多少,可以从 \(dp_{i+1,j}\) 和 \(dp_{i,j+1}\) 转移过来。由于链的两端一定在边界处,最多只有 \(n+m\) 条链,复杂度 \(O(nm(n+m))\)。

#include <bits/stdc++.h>
using namespace std;
const int N=405,inf=1e9;
inline void Max(int &a,int b){if(a<b)a=b;}
inline bool cmp(int x,int y){return x>y;}
int g1[N][N],g2[N][N],n,m,id[N][N],f[N][N];
int sz[N],fa[N];
inline int find_fa(int x){
	return x==fa[x]?x:fa[x]=find_fa(fa[x]);
}
bool flag[N];
inline void merge(int x,int y){
	x=find_fa(x);y=find_fa(y);
	if(x!=y){
		fa[y]=x;sz[x]+=sz[y];
		flag[x]|=flag[y];
	}else{
		flag[x]=1;
	}
}
char s[N];
int b[N],cnt1,c[N],cnt2;
int main(){
	scanf("%d %d",&n,&m);
	int tot=0;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)id[i][j]=++tot,fa[tot]=tot,sz[tot]=1;
	for(int i=1;i<=n+1;++i){
		scanf("%s",s+1);
		for(int j=1;j<=m;++j)g1[i][j]=(s[j]=='1');
	}
	for(int i=1;i<=n;++i){
		scanf("%s",s+1);
		for(int j=1;j<=m+1;++j)g2[i][j]=(s[j]=='1');
	}
	for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
		if(i<n){
			if(!g1[i+1][j])merge(id[i][j],id[i+1][j]);
		}
		if(j<m){
			if(!g2[i][j+1])merge(id[i][j],id[i][j+1]);
		}
	}
	for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
		if(fa[id[i][j]]!=id[i][j])continue;
		if(!g1[i][j]||!g1[i+1][j]||!g2[i][j]||!g2[i][j+1]){
			if(flag[id[i][j]])b[++cnt1]=sz[id[i][j]];
			else c[++cnt2]=sz[id[i][j]];
		}
	}
	sort(b+1,b+1+cnt1,cmp);sort(c+1,c+1+cnt2,cmp);
	for(int i=1;i<=cnt1+cnt2;++i){
		for(int j=0,k;j<=cnt2&&j<=i;++j){
			k=i-j;
			if(k>cnt1)continue;
			f[j][k]=-inf;
			if(j){
				if(c[j]<=2)Max(f[j][k],-f[j-1][k]-c[j]);
				else Max(f[j][k],min(-f[j-1][k]-c[j],f[j-1][k]-c[j]+4));
			}
			if(k)Max(f[j][k],min(-f[j][k-1]-b[k],f[j][k-1]-b[k]+8));
		}
	}
	printf("%d\n",f[cnt2][cnt1]);
	return 0;
}```

标签:2024.3,int,30,fa,en,ans,include,find,模拟
From: https://www.cnblogs.com/xhqdmmz/p/18114473

相关文章

  • c语言:模拟字符串拷贝功能(strcpy),面试题
    面试题:优化中的优化(10分满分)字符串拷贝:是将一个字符串的内容复制到另一个字符串中的操作。运用函数模拟字符串拷贝:(5分)模拟字符串拷贝#include<stdio.h>voidmy_strcpy(char*dest,char*str){ while(*str!='\0') { *dest=*str; str++; dest++; } *dest......
  • Java游戏开发基础:从零开始搭建自己的游戏之《人生重开模拟器》简易版
    一、引言人生重开模拟器游戏是一种虚拟角色扮演游戏,玩家通过控制一个虚构的角色,体验与现实生活中不同的选择和结果。玩家的决策将影响角色的生活轨迹,包括他们的职业生涯、社交关系、健康和财富等方面。游戏的乐趣在于提供了一个虚拟的沙盒环境,玩家可以尝试不同的生活选择,而......
  • 代码随想录算法训练营第二十一天| 530. 二叉搜索树的最小绝对差 501. 二叉搜索树中的
    530.二叉搜索树的最小绝对差https://leetcode.cn/problems/minimum-absolute-difference-in-bst/description/TreeNodepre=null;intres=Integer.MAX_VALUE;publicintgetMinimumDifference(TreeNoderoot){if(root==null)return0;pr......
  • 30 天精通 RxJS (08):简易拖拉实作 - take, first, takeUntil, concatAll
    我们今天要接着讲take,first,takeUntil,concatAll这四个operators,并且实作一个简易的拖拉功能。Operatorstaketake是一个很简单的operator,顾名思义就是取前几个元素后就结束,范例如下varsource=Rx.Observable.interval(1000)varexample=source.take(3)example.......
  • 三分钟一条AI小和尚视频 ,日引300+创业粉,单日变现四位数 ,附赠全套免费工具
    三分钟一条AI小和尚视频 ,日引300+创业粉,单日变现四位数,附赠全套免费工具历经半年,走了无数弯路,轻创社项目网交了无数学费。终于给兄弟们把这个方法测试出来了本次课程非常详细建议大家反复观看简单地说,就是通过ai绘画生成小和尚等人物,并通过一些工具实现视频动态化,发布到......
  • 2-30. 制作 ItemTooltip 的 UI
    增加ItemToolTip最后做成下图的样子就可以了放置字体并生成资源老师使用的字体从上到下分别是:粗宋、龚帆、点阵字体我原来在像素幸存者上面还使用过一个像素字体叫fusion将字体变成FontAsset的时候,需要注意一下字体文件的大小。如果文字太多,但是图片大小不够,就会导......
  • P3038 [USACO11DEC] Grass Planting G
    原题链接题解树上区间修改加单点查询,虽然可以树状数组,但是线段树更通用一点然而线段树通常处理的是点权,可这里是边权,怎么办呢?我们可以把边权转换成点权,由于每个点的子边有若干个,但父边有且只有一个,这样我们就把边权变成边下方点的点权然后区间修改和单点求和的时候把lca的点权......
  • 2024.3.8力扣每日一题——找出美丽数组的最小和
    2024.3.8题目来源我的题解方法一数学题目来源力扣每日一题;题序:2834我的题解方法一数学经过分析,在target之前,取小于等于target/2的正整数才能使得和最小,并且满足条件3。时间复杂度:O(n)空间复杂度:O(n)publicintminimumPossibleSum(intn,inttarget)......
  • CSC330程序设计语言项目
    CSC330程序设计语言项目注1本项目单独完成注2禁止与他人一起工作。注2禁止与他人共享查询或文件。特别注意:剽窃单一或多个来源归属不充分的情况应导致不及格大部分或全部抄袭的作品应获得F级的成绩课程期末将对提交的稿件进行剽窃审查。你要对自己的投稿负责,但如果有人抄袭,你也可......
  • 【问题记录】CCES编译报错:“[Error li1030] Can not open input file ‘libadi_sigma
    一,问题现象编译工程时,报错提示:“[Errorli1030]Cannotopeninputfile‘libadi_sigma_sharc_awc.dlb’”,“[Errorli1030]Cannotopeninputfile‘libadi_sigma_sharc_nwc.dlb’”:二,问题原因&解决方法没有安装对应的插件,安装插件:SigmaStudioForSHARC-SH-Rel2.......