Noi.ac CSP模擬

Noi.ac CSP-S全國模擬賽第三場

mmt

在這裏插入圖片描述

方法

整除分塊
a想同的下標,ll,r=ifloor(i/l)r=\frac{i}{floor(i/l)}
發現b的下標每隔幾個遞增
c[i][j]表示以i結尾間距爲j的前綴和
當左端點大於n\sqrt{n}時區間不大,直接枚舉即可
否則ans+=a[il]c[ilfloor(il)][il]ans+=a[\frac{i}{l}]*c[i-l*floor(\frac{i}{l})][\frac{i}{l}]

心得

最後的公式還是不是太懂!!
整除分塊還不熟

代碼
//整除分塊 預處理 
#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,vu,v兩點最後沒有走到同一點,則可定無法摧毀,考慮求有向圖的lcalca
用拓撲排序來預處理倍增數組
我們發現一個點延伸出去的點的LCALCA即爲這個點的父親,然後正常地求lcalcaOKOK
再記一個depdep數組,lcalcadepdep即爲答案

心得

一直在想怎麼求有向圖的lcalca,還是太菜了啊~~~

代碼
#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]表示前ii位,a==j\sum{a}==j b==k\sum{b}==k c\sum{c}的最小值,顯然是一個揹包,但好像過不去啊,考慮a,ba,b的限制是同一個
f[i][j]f[i][j]表示前iia<=j\sum{a}<=j b>=j\sum{b}>=j c\sum{c}的最小值,可以由[jb[i],ja[i]][j-b[i],j-a[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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章