文章目录
- 前言
- CF789A Anastasia and pebbles
- CF789B Masha and geometric depression
- CF788A Functions again
- CF788B Weird journey
- CF788C The Great Mixing
- CF788D Finding lines
- CF788E New task
前言
789 AB比较水,但是B分情况讨论也可以锻炼严谨思维
788ABC都是码量小也不太难的小清新题,C比较令人耳目一新
788D的思路让人难以想到
788E标准的大数据结构题
CF789A Anastasia and pebbles
Description\text{Description}Description
Anastasia喜欢去公园收集石子.她有 222 个口袋,每个口袋最多装 kkk 个石子.
但她每天只去公园 111 次,而且每次同一个口袋里只装同一种石子.问要装完 nnn 种石子共要花多少天?
输入 nnn,以及每一种石子的数量 wiw_iwi. 输出上述所求答案.
1≤n≤100000;1≤k≤1000000000;1≤wi≤100001\le n\le 100000;1\le k\le 1000000000; 1\le w_i\le100001≤n≤100000;1≤k≤1000000000;1≤wi≤10000
Solution\text{Solution}Solution
求出每种石子需要几袋子装,全部加起来除以二上取整即可.
Code\text{Code}Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define double long double
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
const double eps=1e-12;
inline ll read(){ll 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-'0';c=getchar();}return x*f;
}ll n,m,k;
ll x[N],a[N],sum[N];
ll mn,mx,ans;
signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifn=read();for(int i=1;i<=n;i++) x[i]=read();for(int i=1;i<n;i++) a[i]=abs(x[i]-x[i+1])*((i&1)?1:-1);for(int i=1;i<n;i++){sum[i]=sum[i-1]+a[i];if(i&1) ans=max(ans,sum[i]-mn);else ans=max(ans,mx-sum[i]);mx=max(mx,sum[i]);mn=min(mn,sum[i]);}printf("%lld\n",ans);return 0;
}
/**/
CF789B Masha and geometric depression
Description\text{Description}Description
给你一个等比数列,首项为 b1b_1b1,公比为 qqq.
现在Masha在黑板上从首项开始书写这个等比数列,直到数列某项的绝对值大于 lll,给定 mmm 个整数,若该等比数列中的某项等同于这 mmm 个整数,则不会被写出.
问Masha会写出多少个数字?如果她会写出无穷多个数字,输出inf
.
注意: b1,qb1,qb1,q 可能为000.
Solution\text{Solution}Solution
大特判题.
特判一下公差等于 0/1/−10/1/-10/1/−1,首项等于 000 的情况,其他的暴力算即可.
需要开 longlong.
Code\text{Code}Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define double long double
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
const double eps=1e-12;
inline ll read(){ll 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-'0';c=getchar();}return x*f;
}int n,mx;
ll d,x;
map<ll,bool>mp;
int ans;
signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifx=read();d=read();mx=read();n=read();for(int i=1;i<=n;i++) mp[read()]=1;if(abs(x)>mx){printf("0");return 0;}if(d==0){if(mp[0]) printf("%d",!mp[x]);else printf("inf");}else if(d==1){if(mp[x]) printf("0");else printf("inf");}else if(d==-1){if(mp[x]&&mp[-x]) printf("0");else printf("inf");}else if(x==0){if(mp[0]) printf("0");else printf("inf");}else{while(abs(x)<=mx){if(!mp[x]) ++ans;x*=d;}printf("%d\n",ans);}return 0;
}
/**/
CF788A Functions again
Description\text{Description}Description
定义一个函数,函数如下:
f[l,r]=∑i=lr−1∣ai−ai+1∣×(−1)i−lf[l,r]=\sum^{r-1}_{i=l}|a_i-a_{i+1}|\times (-1)^{i-l}f[l,r]=∑i=lr−1∣ai−ai+1∣×(−1)i−l. (1≤l,r≤n1\le l,r\le n1≤l,r≤n)
∣x∣|x|∣x∣ 表示 xxx 的绝对值.
现在给你一个函数,请取恰当的 l,rl,rl,r 使 fff 值最大,请输出最大的 fff 值.
2≤n≤1052\le n\le 10^52≤n≤105
Solution\text{Solution}Solution
先求出差分数组的绝对值 d1...n−1d_{1...n-1}d1...n−1.
定义:
sumi=∑j=1idj×(−1)j+1sum_i=\sum_{j=1}^i d_j\times(-1)^{j+1}sumi=j=1∑idj×(−1)j+1
那么我们的 fl,rf_{l,r}fl,r 就可以表示为:
(−1)r+1×(sumr−suml−1)(-1)^{r+1}\times (sum_r-sum_{l-1})(−1)r+1×(sumr−suml−1)
所以从前往后扫分别记录 sumsumsum 的最大值和最小值即可.
Description\text{Description}Description
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define double long double
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
const double eps=1e-12;
inline ll read(){ll 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-'0';c=getchar();}return x*f;
}ll n,m,k;
ll x[N],a[N],sum[N];
ll mn,mx,ans;
signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifn=read();for(int i=1;i<=n;i++) x[i]=read();for(int i=1;i<n;i++) a[i]=abs(x[i]-x[i+1])*((i&1)?1:-1);for(int i=1;i<n;i++){sum[i]=sum[i-1]+a[i];if(i&1) ans=max(ans,sum[i]-mn);else ans=max(ans,mx-sum[i]);mx=max(mx,sum[i]);mn=min(mn,sum[i]);}printf("%lld\n",ans);return 0;
}
/**/
CF788B Weird journey
Description\text{Description}Description
总共有 nnn 个节点,mmm 条路径,要求其中 m−2m-2m−2 条路径走两遍,剩下 222 条路径仅走一遍,问不同的路径总数有多少,如果仅走一遍的两条边不同则将这两条路径视为不同.
n,m≤106n,m\le10^6n,m≤106
Solution\text{Solution}Solution
把每条边拆成两个一样的边,考虑欧拉回路的条件.
由于拆了边,显然每个点的度数都是偶数.
所以选择的两条边必须含有公共端点.
考虑自环如何处理.
删掉自环后该点的度数还是偶数,所以剩下的一条边随便删即可.
细节上,需要判断含有边的点必须连通.
Code\text{Code}Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define double long double
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=1e6+100;
const double eps=1e-12;
inline ll read(){ll 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-'0';c=getchar();}return x*f;
}int n,m;
int du[N];
int fa[N],tag[N];
ll num;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifn=read();m=read();for(int i=1;i<=n;i++) fa[i]=i;for(int i=1;i<=m;i++){int x=read(),y=read();fa[find(x)]=find(y);tag[x]=tag[y]=1;if(x==y){++num;continue;}++du[x];++du[y];}int cnt(0);for(int i=1;i<=n;i++){cnt+=(find(i)==i&&tag[i]!=0);}if(cnt>1||m<2){printf("0");return 0;}ll ans(0);for(int i=1;i<=n;i++){ans+=1ll*du[i]*(du[i]-1)/2;//printf("i=%d du=%d ans=%lld\n",i,du[i],ans);}ans+=num*(m-num)+num*(num-1)/2;printf("%lld\n",ans);return 0;
}
/**/
CF788C The Great Mixing
Description\text{Description}Description
有 kkk 种可乐,第 iii 瓶可乐的 CO2 浓度是 ai1000\frac{a_i}{1000}1000ai,问要配置出浓度 n1000\frac{n}{1000}1000n 的可乐,最少需要几瓶可乐.
无解输出 −1-1−1.
0≤n≤1000,k≤1060\le n\le 1000,k\le10^60≤n≤1000,k≤106
Solution\text{Solution}Solution
不难想到把所以值减 nnn 然后转化为加和为 000 的问题.
我的做法是正负两边暴力背包,值域在最差的情况下是 500×499500\times499500×499.
然后就 3×1053\times 10^53×105艹过去了…
现在讲讲 n2n^2n2 的正解.
考虑 bfs.
从 000 开始,向外寻找.
把边界设为 100010001000 即可.
为什么这么是对的?
设答案的组合为 x1+x2+...xk=0x_1+x_2+...x_k=0x1+x2+...xk=0.
不难发现,通过调整前后顺序,一定可以使序列 xxx 的任意一个前缀和的绝对值均不超过 100010001000,那么这个调整后的序列就可以被 bfs 搜到.
因此这样做是正确的.
Code\text{Code}Code
代码还是之前的暴力背包
bfs 不难实现,留给读者自行思考
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define double long double
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=1e6+100;
const double eps=1e-12;
inline ll read(){ll 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-'0';c=getchar();}return x*f;
}int n,k;
int a[1050],b[1050],x,y;
int o=3e5;
int w[N];
int f1[300050],f2[300050];
signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifn=read();k=read();for(int i=1;i<=k;i++) w[i]=read();sort(w+1,w+1+k);k=unique(w+1,w+1+k)-w-1;for(int i=1;i<=k;i++){w[i]-=n;if(w[i]==0){printf("1");return 0;}else if(w[i]>0) a[++x]=w[i];else b[++y]=-w[i];}if(!x||!y){printf("-1");return 0;}memset(f1,0x3f,sizeof(f1));f1[0]=0;for(int i=1;i<=x;i++){int w=a[i];for(int j=w;j<=o;j++) f1[j]=min(f1[j],f1[j-w]+1);}memset(f2,0x3f,sizeof(f2));f2[0]=0;for(int i=1;i<=y;i++){int w=b[i];for(int j=w;j<=o;j++) f2[j]=min(f2[j],f2[j-w]+1);}int ans=2e9;for(int i=1;i<=o;i++) ans=min(ans,f1[i]+f2[i]);printf("%d\n",ans);return 0;
}
/**/
CF788D Finding lines
Description\text{Description}Description
有一个平面,上面有一些水平或竖直的直线.
你每次可以选择一个点,询问距离该点最近的直线到该点的距离.
请求出所有的直线的横/纵坐标.
总直线数不超过 10410^4104.
坐标范围不超过 10810^8108.(询问的点也不能超过该范围)
Solution\text{Solution}Solution
思路很妙的一道题.
不难想到利用 分治.
如何设计分治状态是本题的关键.
第一感似乎是设计 solve(x1,x2,y1,y2)solve(x1,x2,y1,y2)solve(x1,x2,y1,y2) 表示寻找横坐标在 (x1,x2)(x1,x2)(x1,x2) 和纵坐标在 (y1,y2)(y1,y2)(y1,y2) 的直线,然后找到这个矩形的中点尝试.
但是这样分治的两个子结构不互相独立,左边找到的直线也会影响右边,难以处理.
别问我为什么知道难以处理
所以我们要尝试一些特别的设计.
设计 solve(l,r)solve(l,r)solve(l,r) 表示解决坐标在 (l,r)(l,r)(l,r) 的直线(同时包括横纵坐标).
询问中点 (mid,mid)(mid,mid)(mid,mid) .
- 若距离为 000,说明有线经过该点,记录该位置并递归 solve(l,mid−1),solve(mid+1,r)solve(l,mid-1),solve(mid+1,r)solve(l,mid−1),solve(mid+1,r)
- 若距离为 d≠0d\ne0d=0,递归 solve(l,mid−d),solve(mid+d,r)solve(l,mid-d),solve(mid+d,r)solve(l,mid−d),solve(mid+d,r)
最后,我们需要确定步骤一中得到的位置 (p,p)(p,p)(p,p) 经过的是竖线还是横线,记录之前任何一次询问不在线上的点的坐标为 (no,no)(no,no)(no,no),分别询问 (no,p)(no,p)(no,p) 和 (p,no)(p,no)(p,no) 即可.
Code\text{Code}Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define double long double
#define ull unsigned long long
//#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=1e5+100;
const double eps=1e-12;
inline ll read(){ll 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-'0';c=getchar();}return x*f;
}int n,k;
int pl[N],tot;
int no;
int X[N],xx,Y[N],yy;
int o=1e8;
int debug(0);
void solve(int l,int r){if(debug) printf("solve:(%d %d)\n",l,r);if(l>r) return;int mid=(l+r)/2; printf("0 %d %d\n",mid,mid);fflush(stdout);int d=read();if(d==0){if(debug) printf(" ok\n");pl[++tot]=mid;solve(l,mid-1);solve(mid+1,r);}else{no=mid;if(debug) printf(" ??\n");solve(l,mid-d);solve(mid+d,r);}return;
}
signed main(){
#ifndef ONLINE_JUDGE//freopen("a.in","r",stdin);//freopen("a.out","w",stdout);
#endifsolve(-o,o);if(debug) printf("tot=%d no=%d\n",tot,no);for(int i=1;i<=tot;i++){int p=pl[i];printf("0 %d %d\n",no,p);fflush(stdout);if(!read()) Y[++yy]=p; printf("0 %d %d\n",p,no);fflush(stdout);if(!read()) X[++xx]=p;}printf("1 %d %d\n",xx,yy);for(int i=1;i<=xx;i++) printf("%d ",X[i]);putchar('\n');for(int i=1;i<=yy;i++) printf("%d ",Y[i]);return 0;
}
/**/
CF788E New task
Description\text{Description}Description
有一个长度为 nnn 的数列 aia_iai,第 iii 个数有一个零一属性 bib_ibi,初始所有的 bbb 都为 111.
mmm 次操作,分为两种:
1x1 \space x1 x :令 bx=0b_x=0bx=0.
2x2 \space x2 x :令 bx=1b_x=1bx=1
每次操作后输出满足条件的 p1<p2<p3<p4<p5p_1<p_2<p_3<p_4<p_5p1<p2<p3<p4<p5,满足 ap1≤ap2=ap3=ap4≥ap5a_{p_1} \le a_{p_2} =a_{p_3}=a_{p_4} \ge a_{p_5}ap1≤ap2=ap3=ap4≥ap5 且 bp2=bp3=bp4=1b_{p_2}=b_{p_3}=b_{p_4}=1bp2=bp3=bp4=1 的个数.
对 109+710^9+7109+7 取模的值.
(保证每次修改后的 bib_ibi 都与原来不同)
Solution\text{Solution}Solution
很适合练手的数据结构题.
一开始只想到树套树死卡空间然后被 @24KH 一语点破根本不用套.%%%
本题的关键就是动态统计加入一个点的贡献.
设 lil_ili 表示满足 j<i,aj≤aij<i,a_j\le a_ij<i,aj≤ai 的 jjj 的个数.
rir_iri 表示满足 j>i,aj≤aij>i,a_j\le a_ij>i,aj≤ai 的 jjj 的个数.
那么我们考虑加入一个点 ppp 时的贡献:
p 作为中间元素
增加的方案数为:
∑i<p<j∧ai=ap=ajli×rj\large \sum_{i<p<j \land a_i=a_p=a_j}l_i\times r_j i<p<j∧ai=ap=aj∑li×rj
也就是:
∑i<p∧ai=apli×∑j>p∧aj=aprj\large \sum_{i<p \land a_i=a_p}l_i\times \sum_{j>p \land a_j=a_p}r_j i<p∧ai=ap∑li×j>p∧aj=ap∑rj
p 作为两端元素
由于对称,单举 ppp 在最左侧为例.
增加的方案数为:
∑p<i∧ai=aplp×ri×∑p<j<i[aj=ap]\large \sum_{p<i\land a_i=a_p}l_p\times r_i\times\sum_{p<j<i}[a_j=a_p] p<i∧ai=ap∑lp×ri×p<j<i∑[aj=ap]
换句话说就是 (p,i)(p,i)(p,i) 之间每有一个权值相等的点,(p,i)(p,i)(p,i) 作为相等的两端就能产生一次贡献.
统计
离散化后对每个权值建一棵线段树.
∑li/ri\sum l_i/r_i∑li/ri 统计较为简单.
比较困难的就是统计:
∑p<i∧ai=apri×∑p<j<i[aj=ap]\large \sum_{p<i\land a_i=a_p}r_i\times\sum_{p<j<i}[a_j=a_p] p<i∧ai=ap∑ri×p<j<i∑[aj=ap]
这一项.
设 ransransrans 为这一项的答案,lanslanslans 为这一项对称的答案,同时记录数的个数 sizsizsiz,∑li,∑ri\sum l_i,\sum r_i∑li,∑ri
合并左右儿子时:
sizls+sizrs→sizfasiz_{ls}+siz_{rs}\to siz_{fa}sizls+sizrs→sizfa
lsumls+lsumrs→lsumfalsum_{ls}+lsum_{rs}\to lsum_{fa}lsumls+lsumrs→lsumfa
rsumls+rsumrs→rsumfarsum_{ls}+rsum_{rs}\to rsum_{fa}rsumls+rsumrs→rsumfa
lansls+lansrs+lsumls×sizrs→lansfalans_{ls}+lans_{rs}+lsum_{ls}\times siz_{rs}\to lans_{fa}lansls+lansrs+lsumls×sizrs→lansfa
ransls+ransrs+rsumrs×sizls→ransfarans_{ls}+rans_{rs}+rsum_{rs}\times siz_{ls}\to rans_{fa}ransls+ransrs+rsumrs×sizls→ransfa
即可.
Code\text{Code}Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define double long double
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=1e5+100;
const int mod=1e9+7;
inline ll read(){ll 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-'0';c=getchar();}return x*f;
}int n,m;
ll le[N],ri[N];
int id[N],bac[N];#define mid ((l+r)>>1)
struct node{ll siz;ll lsum,rsum;ll lans,rans;
};
node merge(node a,node b){return (node){a.siz+b.siz,(a.lsum+b.lsum)%mod,(a.rsum+b.rsum)%mod,(a.lans+b.lans+a.lsum*b.siz)%mod,(a.rans+b.rans+a.siz*b.rsum)%mod};
}
struct tree{node o;int ls,rs;
}tr[N<<5];
int rt[N],tot;
inline int copy(int x){++tot;tr[tot]=tr[x];return tot;
}
void add(int &k,int l,int r,int p,int w){if(!k) k=copy(k);if(l==r){tr[k].o.siz+=w;tr[k].o.lsum+=w*le[l];tr[k].o.rsum+=w*ri[l];return;}if(p<=mid) add(tr[k].ls,l,mid,p,w);else add(tr[k].rs,mid+1,r,p,w);tr[k].o=merge(tr[tr[k].ls].o,tr[tr[k].rs].o);//printf(" k=%d (%d %d) l")
}
//op=1:lsum
//op=2:lans
//op=3:rsum
//op=4:rans
node ask(int k,int l,int r,int x,int y){if(!k||x>y) return (node){0,0,0,0,0};if(x<=l&&r<=y) return tr[k].o;if(y<=mid) return ask(tr[k].ls,l,mid,x,y);else if(x>mid) return ask(tr[k].rs,mid+1,r,x,y);else return merge(ask(tr[k].ls,l,mid,x,y),ask(tr[k].rs,mid+1,r,x,y));
}int q[N],cnt,a[N];
int f[N];
inline void Add(int p){for(;p<=cnt;p+=p&-p) ++f[p];return;
}
inline int Ask(int p){int res(0);for(;p;p-=p&-p) res+=f[p];return res;
}inline ll calc(int pl){int o=rt[a[pl]];node L=ask(o,1,n,1,pl-1),R=ask(o,1,n,pl+1,n);return (L.lsum*R.rsum%mod+L.lans*ri[pl]%mod+R.rans*le[pl]%mod)%mod;
}
ll tt;
signed main(){
#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);
#endifn=read();for(int i=1;i<=n;i++) q[i]=a[i]=read();sort(q+1,q+1+n);cnt=unique(q+1,q+1+n)-q-1;for(int i=1;i<=n;i++) a[i]=lower_bound(q+1,q+1+cnt,a[i])-q;for(int i=1;i<=n;i++){le[i]=Ask(a[i]);Add(a[i]);}memset(f,0,sizeof(f));for(int i=n;i>=1;i--){ri[i]=Ask(a[i]);Add(a[i]);}for(int i=1;i<=n;i++) id[i]=++bac[a[i]];//for(int i=1;i<=n;i++) printf("i=%d a=%d le=%lld ri=%lld id=%d\n",i,a[i],le[i],ri[i],id[i]);for(int i=1;i<=n;i++){tt+=calc(i);tt%=mod;add(rt[a[i]],1,n,i,1);}m=read();for(int i=1;i<=m;i++){int op=read(),x=read();if(op==1){add(rt[a[x]],1,n,x,-1);tt-=calc(x);}else{tt+=calc(x);add(rt[a[x]],1,n,x,1);}tt+=mod;tt%=mod;printf("%lld\n",tt);}return 0;
}
/**/