8.14 模拟赛小结

前言

最喜欢的一集

T1 儒略历

题意化简:给你一个长度为 \(n\) 的序列 需要挑选 \(4\) 个数 下标为\(A,B,C,D\),满足

  • \(A<B<C<D\)
  • \(A\times B\times C=D\)
  • \(n\leq 10^4\)

这个很简单 枚举 \(C\) 预处理 \(A*B\) 再枚举 \(D\) 时间复杂度 \(O(n^2)\) 能过
Code

#include<bits/stdc++.h>
#define N 10005
#define M 1000005
#define ll long long
using namespace std;
int n,sum[M],cnt[M];
int a[N];
ll ans;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
			if(a[j]%a[i]==0) ans+=1ll*cnt[a[j]/a[i]];
		for(int j=1;j<i;j++)
			if(1ll*a[j]*a[i]<=1000000)cnt[a[j]*a[i]]++;
	}
	cout<<ans;
	return 0;
}

T2 儒略日 原题

题意化简:小Z在坐标 \(0\) 上 然后往右有 \(n\) 个点 每个点有一个坐标和一个时间 如果选择这个点就会少 \(t_i\) 的时间 小Z每个时单位能移动一格 求最后在 \(m\) 的时间内最多能选择几个点

我靠写吐了
第一反应:
选择的点数是单调的!二分答案!
满足的条件就是 \(\sum\limits_{i=1}^ct_i+x_c \leq m\)
枚举一个 \(x_c\) 然后前面的东西一个大根堆搞定
时间复杂度 \(O(n\log^2n)\)
比赛时思考一下其实可以依靠单调性优化一个 \(\log\) 但是\(10^5\)看起来能过
结果赛后居然 TLE 了
我怀疑这是假的 \(10^5\) 还是写一下一个 \(\log\) 的做法
因为 \(x_i\) 为单调上升 所以能满足 \(x_{i-1}\) 必能满足 \(x_i\) 用堆搞就行
Code

#include<bits/stdc++.h>
#define ll long long
#define int long long
#define N 100005
using namespace std;
int n;
ll m;
struct node{
	ll x,t;
}a[N];
bool cmp(node a,node b)
{
	return a.x<b.x;
}
priority_queue <ll> q; 
int cnt,ans;
ll sum;
signed main()
{
	scanf("%d%lld",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%lld%lld",&a[i].x,&a[i].t);
	sort(a+1,a+1+n,cmp);
	ll sum=0;
	for(int i=1;i<=n;i++)
	{
		cnt++,q.push(a[i].t);
		sum+=a[i].t;
		while(!q.empty()&&sum+a[i].x>m)
		{
			sum-=q.top();
			cnt--;
			q.pop();
		}
		ans=max(ans,cnt);
	}
	cout<<ans;
	return 0;
}

T3 儒略月 原题

这个要找找规律
题意:给你一个范围为 \(n,m\) 的矩阵 有 \(q\) 条边联通 \((i,u),(i,v)\)有一条无向边 \(i\leq n\)
\(p\) 条边联通 \((u,i),(v,i)\)有一条无向边 \(i\leq m\) 求删掉边数的权值和最大

这个找找规律 如果数据小用 Kruskal 就行了 但是它太大了 我们要找规律
我们发现 如果一个行上两个点合并了 那么必定每一行两个点就合并的 因此行列的数量是统一减少的
所以我们可以维护两个并查集记录行和列来算即可

Code

#include<bits/stdc++.h>
#define N 100005
#define ll long long
using namespace std;
int n,m,p,q;
int siz[2];
ll ans;
struct node{
	int u,v,w;
	int op;
}a[N*2];
bool cmp(node a,node b)
{
	return a.w<b.w;
}
struct tree{
	int fa[N];
	int find(int x)
	{
		if(fa[x]==x) return x;
		return fa[x]=find(fa[x]);
	}
	void merg(int x,int y)
	{
		x=find(x),y=find(y);
		fa[x]=y;
	}
}tr[2];
int main()
{
	scanf("%d%d%d%d",&n,&m,&p,&q);//0横m个 1竖n个 
	siz[0]=m,siz[1]=n;
	for(int i=1;i<=m;i++) tr[0].fa[i]=i;
	for(int i=1;i<=n;i++) tr[1].fa[i]=i;
	for(int i=1;i<=p;i++)
		scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w),a[i].op=0,ans+=1ll*a[i].w*n;
	for(int i=p+1;i<=p+q;i++)
		scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w),a[i].op=1,ans+=1ll*a[i].w*m;
	sort(a+1,a+1+p+q,cmp);
	for(int i=1;i<=p+q;i++)
	{
		if(tr[a[i].op].find(a[i].u)==tr[a[i].op].find(a[i].v)) continue;
		ans-=1ll*siz[a[i].op^1]*a[i].w;
		siz[a[i].op]--;
		tr[a[i].op].merg(a[i].u,a[i].v);
	}
	cout<<ans;
	return 0;
}

T4 儒略年

题意:给定长度为 \(n\) 的序列 有 \(k\)\(1\) ,给定一个长度为 \(m\) 的数组 \(b\) 每次可以对一个长度为 \(b_i\) 的子序列取反 求所有 \(1\) 变成 \(0\) 的最小操作数

范围:\(n\leq 4\times 10^4,k\leq 11,m\leq 64\)

这题是一道妙题 思维巧码量小 妙妙题!
思路:
凡是这种区间取反 直接用异或差分就行了
将原数列转化为差分数列 最多有 \(2k\)\(1\)
将序列修改转为了修改\(l,r\) 两个点
分类:

  • \(d_l=d_r=0\) 这一定不优 让两个\(0\)变成了\(1\)
  • \(d_l=d_r=1\) 合法操作
  • \(d_l\space xor\space d_r=1\) 这样就是把一个地方的 \(1\) 移到另一个地方
    这样移动我们可以用建图来优化
    连一条 \(j\to j+b_i\) 的边表示可以移动 操作\(l,r\)的最小步骤就是它们的最短路
    为什么?因为异或可以抵消
    求出之后 发现\(k\) 很小 然后状压 dp 即可
    但是会 TLE 因为枚举两个操作点时间顶不住
    我们可以优化状态 表示必须要取最低位 这样就可以优化一个 \(2k\) 就能过了

Code

#include<bits/stdc++.h>
#define N 1000010
#define ll long long
using namespace std;
int n,k,m,a[N],b[N];
int q[N],l,r;
int pos[N],len;
int dis[50][N];
int f[(1<<22)+5];
void bfs(int x)
{
	l=r=0;
	q[++r]=pos[x];
	dis[x][pos[x]]=0;
	while(l<r)
	{
		int now=q[++l];
		int to;
		for(int i=1;i<=m;i++)
		{
			to=now+b[i];
			if(to<=n+1&&dis[x][to]>1e9) dis[x][to]=dis[x][now]+1,q[++r]=to;
			to=now-b[i];
			if(to>=1&&dis[x][to]>1e9) dis[x][to]=dis[x][now]+1,q[++r]=to;
		}	
	}
}
int main()
{
	scanf("%d%d%d",&n,&k,&m);
	for(int i=1;i<=k;i++)
	{
		int x;
		scanf("%d",&x);
		a[x]=1;
	}
	for(int i=1;i<=n+1;i++)
		if(a[i]^a[i-1]) pos[++len]=i;
	for(int i=1;i<=m;i++)
	{
		int x;
		scanf("%d",&b[i]);
	}
	memset(dis,0x3f,sizeof(dis));
	for(int i=1;i<=len;i++)
		bfs(i);
	memset(f,0x3f,sizeof(f));
	f[(1<<len)-1]=0;
	for(int i=(1<<len)-1;i>=0;i--)
	{
		int low=0;
		for(int j=1;j<=len;j++)
		if(i&(1<<j-1))
		{
			low=j;
			break;
		}
		for(int j=low+1;j<=len;j++)
		if(i&(1<<j-1))
		{
			int to=i^(1<<j-1)^(1<<low-1);
			f[to]=min(f[to],f[i]+dis[low][pos[j]]);
		}
	}
	printf("%d",f[0]);
	return 0;
}
posted @ 2023-08-14 21:52  g1ove  阅读(5)  评论(0编辑  收藏  举报