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[i−l∗floor(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]]
[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);
}