Noi.ac CSP-S全國模擬賽第三場
mmt
方法
整除分塊
a想同的下標,,
發現b的下標每隔幾個遞增
c[i][j]表示以i結尾間距爲j的前綴和
當左端點大於時區間不大,直接枚舉即可
否則
心得
最後的公式還是不是太懂!!
整除分塊還不熟
代碼
//整除分塊 預處理
#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
非常妙的一題!!!
題意
方法
若兩點最後沒有走到同一點,則可定無法摧毀,考慮求有向圖的
用拓撲排序來預處理倍增數組
我們發現一個點延伸出去的點的即爲這個點的父親,然後正常地求就
再記一個數組,的即爲答案
心得
一直在想怎麼求有向圖的,還是太菜了啊~~~
代碼
#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
題意
方法
據說是線性規劃?不知道,反正是動態規劃
表示前位, 的最小值,顯然是一個揹包,但好像過不去啊,考慮的限制是同一個
表示前位 的最小值,可以由推來,單調隊列維護
心得
貌似只能是動態規劃,卻還是沒往這方面想~~~
代碼
#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);
}