Noi.ac CSP模拟

Noi.ac CSP-S全国模拟赛第三场

mmt

在这里插入图片描述

方法

整除分块
a想同的下标, l l l, r = i f l o o r ( i / l ) r=\frac{i}{floor(i/l)} r=floor(i/l)i
发现b的下标每隔几个递增
c[i][j]表示以i结尾间距为j的前缀和
当左端点大于 n \sqrt{n} n 时区间不大,直接枚举即可
否则 a n s + = a [ i l ] ∗ c [ i − l ∗ f l o o r ( i l ) ] [ i l ] ans+=a[\frac{i}{l}]*c[i-l*floor(\frac{i}{l})][\frac{i}{l}] ans+=a[li]c[ilfloor(li)][li]

心得

最后的公式还是不是太懂!!
整除分块还不熟

代码
//整除分块 预处理 
#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
#define ll long long
const int N=1e5+4,M=320,mod=123456789;
int n,a[N],b[N],c[N][M],sq;//以n为结尾,间距为m的前缀和 
int main(){
	n=read();sq=sqrt(n);
	for(int i=1;i<=n;i++)a[i]=read()%mod;
	for(int i=0;i<n;i++)b[i]=read()%mod;
	for(int i=0;i<n;i++)
		for(int j=1;j<=sq;j++)
			c[i][j]=i<j?b[i]:((ll)c[i-j][j]+b[i])%mod;
	for(int i=1,x,r,j;i<=n;i++){
		x=0;
		for(int l=1;l<=i;l=r+1){
			j=i/l;r=i/j;
			if(j>sq)for(int k=i-r*j;k<=i-l*j;k++)x=(x+(ll)a[j]*b[k]%mod)%mod;//l,r范围小,暴力枚举 
			else x=(x+(ll)a[j]*c[i-l*j][j]%mod)%mod;//找规律可得 
		}
		printf("%d\n",x);
	}
	return (0-0);
}
sabotage

非常妙的一题!!!

题意

在这里插入图片描述

方法

u , v u,v u,v两点最后没有走到同一点,则可定无法摧毁,考虑求有向图的 l c a lca lca
用拓扑排序来预处理倍增数组
我们发现一个点延伸出去的点的 L C A LCA LCA即为这个点的父亲,然后正常地求 l c a lca lca O K OK OK
再记一个 d e p dep dep数组, l c a lca lca d e p dep dep即为答案

心得

一直在想怎么求有向图的 l c a lca lca,还是太菜了啊~~~

代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=1e5+4;
int fa[N][20],dep[N],out[N],q[N],head=1,tail=0,n,m,Q;
vector<int>zheng[N],fan[N];
inline int lca(int u,int v){
	if(dep[u]<dep[v])swap(u,v);
	int t=dep[u]-dep[v];
	for(int i=0;(1<<i)<=t;i++)
		if((1<<i)&t)u=fa[u][i];
	if(u==v)return u;
	for(int i=19;i>=0;i--)
		if(fa[u][i]!=fa[v][i]){u=fa[u][i];v=fa[v][i];}
	return fa[u][0];
}
int main(){
	n=read();m=read();
	for(int i=1,u,v;i<=m;i++){
		u=read();v=read();
		zheng[u].push_back(v);
		fan[v].push_back(u);
		out[u]++;
	}
	for(int i=1;i<=n;i++)
		if(!out[i])q[++tail]=i;
	while(head<=tail){
		int u=q[head++],p=0;
		if(!zheng[u].empty()){
			p=zheng[u][0];
			for(int i=1;i<zheng[u].size();i++)
				p=lca(p,zheng[u][i]);
		}
		fa[u][0]=p;dep[u]=dep[p]+1;
		for(int i=1;(1<<i)<=dep[u];i++)
			fa[u][i]=fa[fa[u][i-1]][i-1];
		for(int i=0;i<fan[u].size();i++)
			if(--out[fan[u][i]]==0)q[++tail]=fan[u][i];
	}
	Q=read();
	while(Q--){
		int x,y;
		scanf("%d%d",&x,&y);
		printf("%d\n",dep[lca(x,y)]);
	}
	return (0-0);
}
LP
题意

在这里插入图片描述

方法

据说是线性规划?不知道,反正是动态规划
f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示前 i i i位, ∑ a = = j \sum{a}==j a==j ∑ b = = k \sum{b}==k b==k ∑ c \sum{c} c的最小值,显然是一个背包,但好像过不去啊,考虑 a , b a,b a,b的限制是同一个
f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i ∑ a < = j \sum{a}<=j a<=j ∑ b > = j \sum{b}>=j b>=j ∑ c \sum{c} c的最小值,可以由 [ j − b [ i ] , j − a [ i ] ] [j-b[i],j-a[i]] [jb[i],ja[i]]推来,单调队列维护

心得

貌似只能是动态规划,却还是没往这方面想~~~

代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int M=1e4+4,N=1e3+4,inf=2e9;// 
int a[N],b[N],c[N],f[2][M],n,p,q[M],t,head,tail;
int main(){
	t=read();
	while(t--){
		n=read();p=read();
		for(int i=1;i<=p;i++)f[0][i]=f[1][i]=inf;
		for(int i=0;i<n;i++)a[i]=read();
		for(int i=0;i<n;i++)b[i]=read();
		for(int i=0;i<n;i++)c[i]=read();
		for(int i=0;i<n;i++){
			head=1;tail=0;
			for(int j=0;j<=p;j++){
				if(j-a[i]>=0){
					while(head<=tail&&f[i&1][q[tail]]>f[i&1][j-a[i]])tail--;
					q[++tail]=j-a[i];
				}
				if(j-q[head]>b[i])head++;
				f[(i&1)^1][j]=min(f[i&1][j],head<=tail?f[i&1][q[head]]+c[i]:f[i&1][j]);
			}
		}
		if(f[n&1][p]==inf)printf("IMPOSSIBLE!!!\n");
		else printf("%d\n",f[n&1][p]);
	}
	return (0-0);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值