9.22
(1)区间——正解:差分,my:线段树乱搞
我的想法,每次找到最小的点以及其位置,然后把这个点减到0,然后递归左右区间,显然“最小的点以及其位置”可用线段树维护,复杂度O(nlogn)
正解:
将原数组差分,每次操作相当于在一个左边的位置 +1 同时在一个右边 的位置-1,暴力扫描一遍即可
#include <cstdio>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>
#define ls (p<<1)
#define rs (p<<1|1)
#define mid ((l+r)>>1)
#define MP make_pair
using namespace std;
const int N=1e7+5;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n,val[N];
int mi[N],a[N],pos[N];
inline void pushup(int p) {
if(mi[p]>=mi[ls]) mi[p]=mi[ls],pos[p]=pos[ls];
if(mi[p]>=mi[rs]) mi[p]=mi[rs],pos[p]=pos[rs];
}
void build(int l,int r,int p) {
if(l==r) {
mi[p]=a[l];pos[p]=l;
return;
}
build(l,mid,ls);
build(mid+1,r,rs);
pushup(p);
}
int cnt;
pair<int,int>ans[N];
pair<int,int> query(int l,int r,int L,int R,int p) {
if(L>r||l>R) return MP(0x3f3f3f3f,0x3f3f3f3f);
if(L<=l&&r<=R) return MP(mi[p],pos[p]);
if(R<=mid) return query(l,mid,L,R,ls);
else if(L>mid) return query(mid+1,r,L,R,rs);
else {
pair<int,int> f1=query(l,mid,L,R,ls),f2=query(mid+1,r,L,R,rs);
if(f1.first<f2.first) return f1;
else return f2;
}
}
void dfs(int l,int r,int pre) {
if(l>r) return;
pair<int,int> now=query(1,n,l,r,1);
for(int i=1;i<=now.first-pre;i++)
ans[++cnt]=MP(l,r);
if(now.second==l&&l==r) return;
dfs(l,now.second-1,now.first);
dfs(now.second+1,r,now.first);
}
int main() {
n=read();
for(int i=1;i<=n;i++) a[i]=read();
memset(mi,0x3f,sizeof(mi));
build(1,n,1);
dfs(1,n,0);
printf("%d
",cnt);
for(int i=1;i<=cnt;i++)
printf("%d %d
",ans[i].first,ans[i].second);
return 0;
}
正解:
和积木大赛其实是一样的,再输出个方案数就行
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e5+5;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n,a[N],st[N],tp,f[N];
int main() {
n=read();
for(int i=1;i<=n;i++) a[i]=read();
int pre=a[1],now=0;
for(int i=2;i<=n;i++) {
if(a[i]<=a[i-1]) now=pre;
else now=pre+a[i]-a[i-1];
pre=now;
}
printf("%d
",now);
for(int i=1;i<=n;i++) {
if(a[i]>a[i-1]) {
for(int j=1;j<=a[i]-a[i-1];j++)
st[++tp]=i;
}
if(a[i]>a[i+1]) {
for(int j=1;j<=a[i]-a[i+1];j++)
printf("%d %d
",st[tp--],i);
}
}
return 0;
}
相似的题目
IncDec Sequence
参考这个https://www.cnblogs.com/Leohh/p/7655155.html
#include <cstdio>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=100005;
typedef long long ll;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n,a[N];
ll jue(ll x){return x>0?x:(-x);}
int main() {
n=read();
for(int i=1;i<=n;i++) a[i]=read();
ll pos=0,neg=0;
for(int i=2;i<=n;i++)
if(a[i]>a[i-1]) pos+=a[i]-a[i-1];
else neg+=a[i-1]-a[i];
printf("%lld
%lld
",max(pos,neg),jue(pos-neg)+1);
return 0;
}
(2)计数+容斥/背包
45分快乐暴力
#include <cstdio>
#include <vector>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=105;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n,m,cnt;
long long ans;
int p[N],a[N];
int main() {
n=read();m=read();p[0]=0;
if(m==0) {
printf("0");return 0;
}
for(int i=1;i<=n;i++)
if(n%i==0) {
++p[0];p[p[0]]=i;
}
if(m==1) {
for(int i=1;i<=p[0];i++)
for(int j=1;j<=p[0];j++)
if(p[i]*p[j]<=n)
ans++;
printf("%d
",ans);
return 0;
}
if(m==2) {
int mx=n*n;
for(int i1=1;i1<=p[0];i1++)
for(int i2=1;i2<=p[0];i2++)
for(int i3=1;i3<=p[0];i3++)
for(int i4=1;i4<=p[0];i4++)
if(p[i1]*p[i2]*p[i3]*p[i4]<=mx)
ans++;
printf("%d
",ans%998244353);return 0;
}
if(m==3) {
int mx=n*n*n;
for(int i1=1;i1<=p[0];i1++) {
for(int i2=1;i2<=p[0];i2++) {
if(p[i1]*p[i2]>mx) break;
for(int i3=1;i3<=p[0];i3++) {
if(p[i1]*p[i2]*p[i3]>mx) break;
for(int i4=1;i4<=p[0];i4++) {
if(p[i1]*p[i2]*p[i3]*p[i4]>mx) break;
for(int i5=1;i5<=p[0];i5++) {
if(p[i1]*p[i2]*p[i3]*p[i4]*p[i5]>mx) break;
for(int i6=1;i6<=p[0];i6++)
if(p[i1]*p[i2]*p[i3]*p[i4]*p[i5]*p[i6]<=mx)ans++;
else break;
}
}
}
}
}
printf("%d
",ans%998244353);return 0;
}
return 0;
}
接着solution
然后最后那个背包可以转化成容斥
[质因数分解\
prod_{i=1}^{2m} x[i]^{k[i]}=n^m\
设t[i]是n的因数p[i]分解后的指数\
我们要找sum_{i=1}^{2m}t[i]=m*k[1]~(t[i]<=k[1])\
然后问题转化成了2m个盒子,放m*k - i * (k + 1),有i个大于等于(k+1)的t[],隔板法可以为空,
]
#include <iostream>
#include <cassert>
#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
const int MAXN=5005;
const int P=998244353;
int n,m,ans,ans1,ans2=1,fac[MAXN],inv[MAXN];
inline int read() {
int x=0;
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x;
}
int qpow(int x,int b) {
long long res=1,a=x%P;//注意要化成统一类型
while(b){
if(b&1) res=res*a%P;
a=a*a%P;
b>>=1;
}
return res;
}
inline int get() {
int sum=0;
for (int i=1;i*i<=n;i++) {
if(n%i) continue;
sum++,sum+=(i*i!=n);
}
return sum%P;
}//num_factor
inline int C(int x,int y) {
if(x<0||y<0||y>x) return 0;
return 1ll*fac[x]*inv[y]%P*inv[x-y]%P;
}
inline int calc(int k) {//容斥
int sum=0;
for (int i=0;i<=m;i++)
if(i&1) sum+=P-1ll*C(m*2,i)*C(k*m-i*(k+1)+m*2-1,m*2-1)%P,sum%=P;
else sum+=1ll*C(m*2,i)*C(k*m-i*(k+1)+m*2-1,m*2-1)%P,sum%=P;
return sum;
}
int main() {
n=read(),m=read();
ans1=qpow(get(),m*2);
fac[0]=1;
for (int i=1;i<=5000;i++)
fac[i]=1ll*fac[i-1]*i%P;
inv[5000]=qpow(fac[5000],P-2);
for (int i=5000;i;i--)
inv[i-1]=1ll*inv[i]*i%P;
for (int i=2;i*i<=n;i++) {
int tot=0;
if(n%i) continue;
while(n%i==0) tot++,n/=i;
ans2=1ll*ans2*calc(tot)%P;
}//get_factor
if(n!=1) ans2=1ll*ans2*calc(1)%P;
printf("%lld
",1ll*(ans1+ans2)*qpow(2,P-2)%P);
return 0;
}
(3)train——正解:并查集 My:树剖
自闭了——审题错误。。。以为只把m里 的经过的打标记——实际上是路径上的所有点都要打标记,线段树区间加95分不翼而飞
实际100分只需搞成树状数组就好
树状数组优点是跑的飞快(写的简单),缺点就是占空间大
总体复杂度是O(nlogn^2)——树剖log+树状数组不到Log
#include <queue>
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=500005;
const int M=1000005;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
inline void Max(int &x,int y){if(x<y)x=y;}
inline void Min(int &x,int y){if(x>y)x=y;}
int n,m;
int hd[N],nxt[M],to[M],tot;
inline void add(int x,int y) {
to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}
int fa[N],dep[N],son[N],siz[N];
void dfs_son(int x,int f) {
fa[x]=f;dep[x]=dep[f]+1;siz[x]=1;
for(int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(y==f) continue;
dfs_son(y,x);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
}
int top[N],dfn[N],rev[N],dfn_cnt;
void dfs_chain(int x,int tp) {
top[x]=tp;
dfn[x]=++dfn_cnt;rev[dfn_cnt]=x;
if(son[x]) dfs_chain(son[x],tp);
for(int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(y==son[x]||y==fa[x]) continue;
dfs_chain(y,y);
}
}
int dist(int x,int y) {
int res=dep[x]+dep[y];
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
int lca=dep[x]<dep[y]?x:y;
return res-2*dep[lca];
}
int t[N*2];
void upd(int x,int k) { for(;x<=n;x+=x&(-x))t[x]+=k;}
int query(int x) {int ans=0;for(;x;x-=x&(-x))ans+=t[x];return ans;}
void modify(int x,int y,int z) {
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) swap(x,y);
upd(dfn[top[x]],z);upd(dfn[x]+1,-z);//区间修改
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
upd(dfn[x],z);upd(dfn[y]+1,-z);
}
int st;
bool vis[N];
long long ans;
int main() {
// freopen("3.in","r",stdin);
// freopen("3.out","w",stdout);
n=read();m=read();st=read();
for(int i=1,x,y;i<n;i++) {
x=read();y=read();
add(x,y);add(y,x);
}
dfs_son(1,0);
dfs_chain(1,1);
for(int i=1,x;i<=m;i++) {
x=read();
//query(dfn[x])单点查询
if(query(dfn[x])) continue;
modify(st,x,1);
ans+=dist(st,x);
st=x;
}
printf("%lld
",ans);
return 0;
}
当然了正解其实更妙
并查集维护——我们要把x——y上的点都标记,然后我们可以拆成两条链,一条x——lca,一条y——lca,然后暴力跳find跳fa,这样的话我们把路径上的fa[x]都改成了fa[lca],然后每次查询时如果find(x)!=x则说明其被标记过,直接continue
这样我们发现每个点都是只被经过一次,加上并查集复杂度O(logn),总共O(nlogn)
#include <queue>
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=500005;
const int M=1000005;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n,m;
int hd[N],nxt[M],to[M],tot;
inline void add(int x,int y) {
to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
}
int fa[N],dep[N],son[N],siz[N];
void dfs_son(int x,int f) {
fa[x]=f;dep[x]=dep[f]+1;siz[x]=1;
for(int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(y==f) continue;
dfs_son(y,x);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
}
int top[N],dfn[N],rev[N],dfn_cnt;
void dfs_chain(int x,int tp) {
top[x]=tp;
dfn[x]=++dfn_cnt;rev[dfn_cnt]=x;
if(son[x]) dfs_chain(son[x],tp);
for(int i=hd[x];i;i=nxt[i]) {
int y=to[i];
if(y==son[x]||y==fa[x]) continue;
dfs_chain(y,y);
}
}
int LCA(int x,int y) {
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
int st;
long long ans;
int f[N];
inline int find(int x) {
return x==f[x]?x:f[x]=find(f[x]);
}
int main() {
// freopen("3.in","r",stdin);
// freopen("3.out","w",stdout);
n=read();m=read();st=read();
for(int i=1,x,y;i<n;i++) {
x=read();y=read();
add(x,y);add(y,x);
}
dfs_son(1,0);
dfs_chain(1,1);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1,x;i<=m;i++) {
x=read();
if(find(x)!=x) continue;
int lca=LCA(x,st);
ans+=dep[x]+dep[st]-2*dep[lca];
int fx=find(x),fy=find(st);
while(dep[fx]>=dep[lca])
f[fx]=fa[fx],fx=find(fx);
while(dep[fy]>=dep[lca])
f[fy]=fa[fy],fy=find(fy);
st=x;
}
printf("%lld
",ans);
return 0;
}