A:Alyona and copybooks
题目大意:已经给了你n本书,你可以选择花a元买一本书,花b元买两本书(严格两本),花c元买三本书(严格三本),让你花最少的钱买x本书使得n+x为4的倍数。
思路:枚举一下买一本书,两本书,三本书的次数,然后统计答案即可。
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define inf 1e18 int n,a,b,c; inline int read(){ int x=0,f=1;char ch=getchar(); for (;ch<'0'||ch>'9';ch=getchar())if (ch=='-') f=-1; for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; return x*f; } inline void print(int x){ if (x>=10) print(x/10); putchar(x%10+'0'); } int main(){ n=read(),a=read(),b=read(),c=read(); long long ans=inf; for (int i=0;i<=3;i++) for (int j=0;j<=3;j++) for (int k=0;k<=3;k++) if ((i+j*2+k*3+n)%4==0) ans=min(ans,1ll*a*i+1ll*b*j+1ll*c*k); printf("%lld ",ans); return 0; }
B:Alyona and flowers
题目大意:给你n个数,m个区间,然后对于所有的区间可以选也可以不选,每个区间对答案的贡献为这个区间的权值之和,问怎样选择能使答案尽量大。
思路:统计一下每个区间的答案,显然大于0就要加到答案里来。
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define maxn 105 int n,m,ans; int sum[maxn]; inline int read(){ int x=0,f=1;char ch=getchar(); for (;ch<'0'||ch>'9';ch=getchar())if (ch=='-') f=-1; for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; return x*f; } inline void print(int x){ if (x>=10) print(x/10); putchar(x%10+'0'); } int main(){ n=read(),m=read(); for (int i=1;i<=n;i++) sum[i]=sum[i-1]+read(); for (int i=1;i<=m;i++){ int l=read(),r=read(); ans+=max(0,sum[r]-sum[l-1]); } printf("%d ",ans); return 0; }
C:Alyona and mex
题目大意:给出n和m,和m个区间,让你求出一个长度为n的一个数组,让所有区间的mex的最小值尽量大,同时也要给出这个最大的mex值。
对一个集合S求mex即求出不属于集合S的最小非负整数。
思路:首先答案不可能会超过最短区间的长度(这个根据mex的定义可以得出),然后设最短区间长度为len,那么可以构造出一种方案,使得对于任意的区间[i,i+len-1](i∈[1,n]&&i+len-1∈[1,n]),它们的mex值均为len,这样构造的方案也一定合法,因为更长的区间答案也一定是len(可以看作更长的区间由一个长度为len的区间和另外一个区间组成),那么这样显然第一问的答案就是len,因为更大的答案是不可能的,而我们恰巧能构造出答案为len的数组。
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define maxn 100005 #define inf 1e9 int n,m,ans=inf; int a[maxn]; inline int read(){ int x=0,f=1;char ch=getchar(); for (;ch<'0'||ch>'9';ch=getchar())if (ch=='-') f=-1; for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; return x*f; } inline void print(int x){ if (x>=10) print(x/10); putchar(x%10+'0'); } int main(){ n=read(),m=read(); for (int i=1,l,r;i<=m;i++) l=read(),r=read(),ans=min(ans,r-l+1); for (int i=1;i<=ans;i++) a[i]=i-1; for (int i=ans+1;i<=n;i++) a[i]=a[i-ans]; print(ans);puts(""); for (int i=1;i<=n;i++) print(a[i]),putchar(' '); puts(""); return 0; }
D:Alyona and a tree
题目大意:给出一个n个点以1为根的树,每个点有一个点权(记为wi),同时每条边有条边权,然后定义dist(i,j)表示点i和点j的距离,然后对于一个点i,要求出在以它为根的子树中有多少个点j满足dist(i,j)<=wj。
思路:单独考虑每一个点对答案的贡献,显然这个点只能对其祖先结点产生贡献,又因为该点到其越远的祖先的dist越大,满足单调性,然后用个倍增找到它最远能对哪个祖先产生贡献,然后打个标记差分一下即可。
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define maxn 200005 int n,m,tot; int w[maxn],son[maxn*2],pre[maxn*2],val[maxn*2],now[maxn]; long long dis[maxn]; int dep[maxn],f[maxn][22],ans[maxn]; inline int read(){ int x=0,f=1;char ch=getchar(); for (;ch<'0'||ch>'9';ch=getchar())if (ch=='-') f=-1; for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; return x*f; } inline void print(int x){ if (x>=10) print(x/10); putchar(x%10+'0'); } void add(int a,int b,int c){ son[++tot]=b; pre[tot]=now[a]; now[a]=tot; val[tot]=c; } void link(int a,int b,int c){ add(a,b,c),add(b,a,c); } void dfs(int x,int fa){ dep[x]=dep[fa]+1; for (int p=now[x];p;p=pre[p]) if (son[p]!=fa) f[son[p]][0]=x,dis[son[p]]=dis[x]+val[p],dfs(son[p],x); } void getsum(int x,int fa){ for (int p=now[x];p;p=pre[p]) if (son[p]!=fa) getsum(son[p],x),ans[x]+=ans[son[p]]; } int main(){ n=read();int l=log2(n); for (int i=1;i<=n;i++) w[i]=read(); for (int i=1;i<n;i++){ int a=read(),b=read(); link(i+1,a,b); } dfs(1,0); for (int i=1;i<=l;i++) for (int x=1;x<=n;x++) f[x][i]=f[f[x][i-1]][i-1]; for (int i=1;i<=n;i++){ int t=log2(dep[i]),a=i; for (;dis[i]-dis[f[a][0]]<=w[i]&&f[a][0];){ for (;!f[a][t]||dis[i]-dis[f[a][t]]>w[i];t--); a=f[a][t]; } ans[f[i][0]]++,ans[f[a][0]]--; } getsum(1,0); for (int i=1;i<=n;i++) print(ans[i]),putchar(' ');puts(""); return 0; }
E:Alyona and towers
题目大意:给出一个序列,然后每个有m次操作,每次将一段区间的数加上一个数,然后每次操作后询问当前序列中最长的区间[l,r]且满足a[l]<a[l+1]<a[l+2]<...<a[mid-1]<a[mid]>a[mid+1]>...>a[r-2]>a[r-1]>a[r]
思路:之前想法是用线段树维护,记lmax,rmax,ans去搞,然后发现没法知道每次操作后某一个点的值(更新lmax,rmax要用),然后就GG了。。。
然后正解也是维护lmax,rmax,ans,不过不是搞原序列,而是搞出差分序列,即用线段树维护a',其中a'[i]=a[i+1]-a[i]。
这样每次修改只需要把a[l-1]+=val,a[r]-=val即可,那么就可以快速维护lmax,rmax,ans然后用线段树维护即可。
#include<cmath> #include<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define maxn 300005 int n,m; long long a[maxn]; inline int read(){ int x=0,f=1;char ch=getchar(); for (;ch<'0'||ch>'9';ch=getchar())if (ch=='-') f=-1; for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; return x*f; } inline void print(int x){ if (x>=10) print(x/10); putchar(x%10+'0'); } struct segment_tree{ struct treenode{ int size,lmax,rmax,ans; }tree[4*maxn]; void update(int p,int x){ tree[p].lmax=tree[p<<1].lmax,tree[p].rmax=tree[p<<1|1].rmax; tree[p].ans=max(tree[p<<1].ans,tree[p<<1|1].ans); if (!a[x]||!a[x+1]||(a[x]<0&&a[x+1]>0)) return; if (tree[p].lmax==tree[p<<1].size) tree[p].lmax+=tree[p<<1|1].lmax; if (tree[p].rmax==tree[p<<1|1].size) tree[p].rmax+=tree[p<<1].rmax; tree[p].ans=max(tree[p].ans,tree[p<<1].rmax+tree[p<<1|1].lmax); } void build(int p,int l,int r){ tree[p].size=r-l+1; if (l==r){ tree[p].lmax=tree[p].rmax=tree[p].ans=1; return; } int mid=(l+r)>>1; build(p<<1,l,mid),build(p<<1|1,mid+1,r); update(p,mid); } void change(int p,int l,int r,int pos,int val){ if (l==r){ a[l]+=val; return; } int mid=(l+r)>>1; if (pos<=mid) change(p<<1,l,mid,pos,val); else change(p<<1|1,mid+1,r,pos,val); update(p,mid); } }T; int main(){ n=read(); for (int i=1;i<=n;i++) a[i]=read(); for (int i=1;i<n;i++) a[i]=a[i+1]-a[i]; n--; m=read(); if (!n){ for (int i=1;i<=m;i++) puts("1"); return 0; } T.build(1,1,n); for (int i=1;i<=m;i++){ int l=read(),r=read(),val=read(); if (l>1) T.change(1,1,n,l-1,val); if (r<=n) T.change(1,1,n,r,-val); print(T.tree[1].ans+1),puts(""); } return 0; }