2024.8.19 模拟赛 24

模拟赛

总是忘记保存怎么办

难得挂分。

T1 AND and SUM

签到题,如果两数按位与结果为 \(a\),那么它们的二进制重复为 \(1\) 的位一定就是 \(a\) 的二进制为 \(1\) 的位置,

所以它们相加的值至少是 \(2a\)。并且不够的差值只能在 \(a\) 二进制为零的位置补(否则会有进位),所以判 \((s-2a)\&a\) 是否为零。

赛时判了很多东西。。。

code
#include<bits/stdc++.h>
using namespace std;
#define LL __int128
int T;
LL a,s;
inline LL read()
{
	LL res=0; char x=getchar();
	while(x<'0'||x>'9') x=getchar();
	while(x>='0'&&x<='9') res=(res<<1)+(res<<3)+(x^48),x=getchar();
	return res;
}
int main()
{
	freopen("and.in","r",stdin);
	freopen("and.out","w",stdout);
	scanf("%d",&T);
	while(T--)
	{
		a=read(),s=read();
		if(a==0) printf("Yes\n");
		else if(s==0) printf("No\n");
		else if((a<<1)>s) printf("No\n");
		else if((a<<1)==s) printf("Yes\n");
		else
		{
			if((s-(a<<1))&a) printf("No\n");
			else printf("Yes\n");
		}
	}
	return 0;
}

T2 Maximum Composition

前两天刚见过的题,幸好赛时想出来了,虽然唐氏挂分。

先考虑如果确定选 \(f_1,f_2\),那么什么顺序是最优的。

  • 先选 \(f_1\)\(a_2(a_1x+b_1)+b_2=a_1a_2x+a_2b_1+b_2\)

  • 先选 \(f_2\)\(a_1(a_2x+b_2)+b_1=a_1a_2x+a_1b_2+b_1\)

所以如果 \((a_1-1)b_2<(a_2-1)b_1\),那么先选 \(f_1\) 更优。

所以我们按上述标准排序,那么得到的就是在有序的数列上选 \(k\) 个函数使结果最大。那么可以直接 DP。

注意初始化!!!

code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 2e5+5;
int n,k;
LL f[N][11];
struct A
{
	int a,b;
	bool operator < (const A &x) const
	{
		return 1.0*(a-1)/b<1.0*(x.a-1)/x.b;
	}
} s[N];

int main()
{
	freopen("func.in","r",stdin);
	freopen("func.out","w",stdout);
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d%d",&s[i].a,&s[i].b);
	sort(s+1,s+1+n); f[0][0]=1;//!!!!!!!!!!!!!!
	for(int i=1;i<=n;i++)
	{
		f[i][0]=1;
		for(int j=1;j<=min(k,i);j++)
		{
			f[i][j]=max(f[i-1][j-1]*s[i].a+s[i].b,f[i-1][j]);
		}
	}
	printf("%lld",f[n][k]);
	return 0;
}

T3 kangaroo

怎么想到这是预设型 DP 啊!

预设型 DP,也叫插入法 DP(这个更合适)。

我们先转化题意,将每次跳跃的点的编号组成一个序列。那么这个序列一定是波浪形的。

也就是一大一小交替放的序列的个数。考虑插入法 DP。也就是考虑填数所形成的连续段。

我们按点的编号从小到大填,这样有一个显然性质是后填的数一定比先填的大!(够显然吧)。

那么如果得到结论:填的数只能单独成段或者连接另外两个连通块

否则一定会出现一段单调的区间,这是非法的。

所以设计状态为 \(f_{i,j}\) 表示填前 \(i\) 个数形成 \(j\) 个连续段的方案数。

转移比较简单,注意特判终点和起点,它们必须在序列两端。

还有线性 \(O(n)\) 做法,如果有机会??? CEOI2016 Kangaroo 线性做法

code
#include<bits/stdc++.h>
using namespace std;
const int N = 2e3+5,mod = 1e9+7;
int n,s,t;
int f[2][N];
int main()
{
	// freopen("kang.in","r",stdin);
	// freopen("kang.out","w",stdout);
	scanf("%d%d%d",&n,&s,&t);
	f[1][1]=1;
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		{
			if(i==s||i==t) f[i&1][j]=(1ll*f[(i-1)&1][j-1]+f[(i-1)&1][j])%mod;
			else
			{
				f[i&1][j]=(1ll*f[(i-1)&1][j+1]*j+1ll*f[(i-1)&1][j-1]*(j-(i>s)-(i>t)))%mod;
			}
		}
	}
	printf("%d\n",f[n&1][1]);
	return 0;
}

T4 The Classic Problem

一眼高精度。

\(2^{1e5}\) 显然不能用常规方法做。但是 dij 求最短路是肯定的,所以我们考虑如果想实现 dij 需要维护哪些操作。

我们正常的 \(dij\) 需要维护的操作:加法比较大小

加法每次只会在二进制的某一位加一。如果加的这一位恰好为 \(1\),那么考虑进位,找出向后连续的最长的 \(1\)。如果为 \(0\),那直接加即可。

比较大小则先比较高位,不同再比较低位。

并且我们应该要能记录每一个出现的数。

上述操作都可以用主席树实现,用主席树维护 \(1e5\) 的二进制位。

由于比较的操作需要判断区间是否相等,所以考虑直接用 hash 维护,这样的话加的时候注意对齐。

单点加区间推平直接搞,查找连续的 \(1\) 线段树上二分即可,其他的正常 dij。

code
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5,H = 1e9+3579,B = 233,mod = 1e9+7,V = 2e5+5;
int n,m,s,t;
int head[N],tot,p[N],p2[N],num,rt[N];
struct E {int u,v,w;} e[N<<1];
inline void add(int u,int v,int w) {e[++tot]={head[u],v,w}; head[u]=tot;}
inline void init() {p[0]=p2[0]=1; for(int i=1;i<N;i++) p[i]=1ll*p[i-1]*B%H,p2[i]=p2[i-1]*2ll%mod;}
namespace SEG
{
	struct T {int l,r,sum,hs,len;} tr[N<<7];
	inline void pushup(int k)
	{
		tr[k].len=tr[tr[k].l].len+tr[tr[k].r].len;
		tr[k].sum=tr[tr[k].l].sum+tr[tr[k].r].sum;
		tr[k].hs=(1ll*tr[tr[k].l].hs*p[tr[tr[k].r].len]%H+tr[tr[k].r].hs)%H;
	}	
	inline void bui(int &k,int l,int r,int v)
	{
		k=++num;
		if(l==r) return (tr[k].len=1,tr[k].hs=tr[k].sum=v),void(0);
		int mid=l+r>>1;
		bui(tr[k].l,l,mid,v); bui(tr[k].r,mid+1,r,v);
		pushup(k);
	}
	inline void mdf(int &k,int pre,int l,int r,int p,int v)
	{
		k=++num; tr[k]=tr[pre];
		if(l==r) return (tr[k].sum=tr[k].hs=v),void(0);
		int mid=l+r>>1;
		if(p<=mid) mdf(tr[k].l,tr[pre].l,l,mid,p,v);
		else mdf(tr[k].r,tr[pre].r,mid+1,r,p,v);
		pushup(k);
	}
	void assign(int &k,int pre,int co,int l,int r,int L,int R)
	{
		if(L<=l&&r<=R) return k=co,void(0);
		k=++num; tr[k]=tr[pre];
		int mid=l+r>>1;
		if(L<=mid) assign(tr[k].l,tr[pre].l,tr[co].l,l,mid,L,R);
		if(R>mid) assign(tr[k].r,tr[pre].r,tr[co].r,mid+1,r,L,R);
		pushup(k);
	}
	int cmp(int x,int y,int l,int r)
	{
		if(l==r) return tr[x].hs-tr[y].hs;
		int mid=l+r>>1;
		if(tr[tr[x].r].hs!=tr[tr[y].r].hs) return cmp(tr[x].r,tr[y].r,mid+1,r);
		return cmp(tr[x].l,tr[y].l,l,mid);
	}
	int afind(int k,int l,int r)
	{
		if(tr[k].sum==tr[k].len) return r;
		if(l==r) return -1;
		int mid=l+r>>1;
		int x=afind(tr[k].l,l,mid);
		if(x==mid) return max(x,afind(tr[k].r,mid+1,r));
		return x;
	}
	int find(int k,int l,int r,int p)
	{
		if(p<=l) return afind(k,l,r);
		int mid=l+r>>1;
		if(p<=mid)
		{
			int x=find(tr[k].l,l,mid,p);
			if(x==mid) return  max(x,find(tr[k].r,mid+1,r,p));
			return x;
		}
		return find(tr[k].r,mid+1,r,p);
	}
}; using namespace SEG;

inline int ad(int k,int p)
{
	int x=find(k,0,V,p),r=0,res=0; 
	if(x<0) x=p-1,r=k;
	else assign(r,k,rt[0],0,V,p,x);
	mdf(res,r,0,V,x+1,1);
	return res;
}
int d[N];
bool vs[N];
inline void dj(int s)
{
	struct node
	{
		int u,id;
		bool operator < (const node &x) const
		{
			return cmp(id,x.id,0,V)>0;
		}
	};
	priority_queue<node> q; bui(rt[0],0,V,0); bui(rt[n+1],0,V,1);
	for(int i=1;i<=n;i++) rt[i]=rt[n+1];
	rt[s]=rt[0]; q.push({s,rt[s]}); d[s]=0;
	while(!q.empty())
	{
		int u=q.top().u,id=q.top().id; q.pop();
		if(vs[u]) continue; vs[u]=1;
		for(int i=head[u];i;i=e[i].u)
		{
			int v=e[i].v;
			int now=ad(id,e[i].w);
			if(!vs[v]&&cmp(now,rt[v],0,V)<0) 
			{
				d[v]=(d[u]+p2[e[i].w])%mod; q.push({v,rt[v]=now});
			}
		}
	}
}
int main()
{
	freopen("hellagur.in","r",stdin);
	freopen("hellagur.out","w",stdout);
	scanf("%d%d",&n,&m); init(); 
	for(int i=1;i<=m;i++)
	{
		int x,y,z; scanf("%d%d%d",&x,&y,&z);
		add(x,y,z); add(y,x,z);
	}
	scanf("%d%d",&s,&t);
	dj(s);
	printf("%d\n",vs[t]?d[t]:-1);
	return 0;
}
posted @ 2024-09-23 20:18  ppllxx_9G  阅读(11)  评论(0编辑  收藏  举报