*1700
Monoblock - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
首先看数据范围 1≤n,m≤1e5。
主要是修改1e5,查询1e5,这里的话要么O (log) 做法,要么O(1)做
O(log)没有什么好方法,那就是O(1)做法。
通过O(1)做法,联想单点修改,单点统计。(套路,但要学过)
这道题比较妙的点:
比如对于统计 [1,7,7,7,7,7,7,7,9,9,9,9,9,9,9,9,9] ,我们把连续相同数字的第一个数,作为对答案的贡献值
现在我们枚举第i位的a[i]对答案的贡献,O(1)统计
一:对于 [i~i] , [i~i+1] , [i~i+2] , [i~i+3] …… [i~n-1],[i~n]的这些序列,i下标就是一个出现a[i]值的位置,对于答案的贡献就是 n-i+1
二:再考虑(1~i-1)的位置,如果a[i-1]!=a[i],那么对(1~i-1)有贡献,否则a[i]对(1~i-1)不会有贡献,因为有a[i-1]的存在
那么如果a[i-1]!=a[i],对于(1~i-1)的位置,根据数学组合,应该有 len(1,i-1)(枚举左端点~a[i])len(i,n)(a[i]~枚举右端点)
ll calc(int i) { //这里的关键:例如:对于 1,2,2,2,3,3,3,3 我们比第一个出现的当作贡献值,下标 1,2,5 就是对答案的贡献 if(a[i]==a[i-1]) return n-i+1; else return 1LL*(i-1)*(n-i+1)+n-i+1; }
对于修改,如果对第i位进行了修改,那么只会对第i位和第i+1的答案贡献有影响,我们只需先删去原本的答案贡献,再加上修改后的答案贡献即可
for(int i=1;i<=m;i++) { int k=read(),x=read(); ans-=calc(k)+calc(k+1); a[k]=x; ans+=calc(k)+calc(k+1); printf("%lld\n",ans); }
Code:
#include<bits/stdc++.h> using namespace std; #define ll long long #define mp make_pair #define pb push_back //vector函数 #define popb pop_back //vector函数 #define fi first #define se second const int N=1e5+10; //const int M=; //const int inf=0x3f3f3f3f; //一般为int赋最大值,不用于memset中 //const ll INF=0x3ffffffffffff; //一般为ll赋最大值,不用于memset中 int T,n,m,a[N]; ll sum[N]; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } ll calc(int i) { //这里的关键:例如:对于 1,2,2,2,3,3,3,3 我们比第一个出现的当作贡献值,下标 1,2,5 就是对答案的贡献 if(a[i]==a[i-1]) return n-i+1; else return 1LL*(i-1)*(n-i+1)+n-i+1; } int main() { // freopen("","r",stdin); // freopen("","w",stdout); n=read(),m=read(); for(int i=1;i<=n;i++) a[i]=read(); ll ans=0; for(int i=1;i<=n;i++) ans+=calc(i); for(int i=1;i<=m;i++) { int k=read(),x=read(); ans-=calc(k)+calc(k+1); a[k]=x; ans+=calc(k)+calc(k+1); printf("%lld\n",ans); } return 0; }
标签:ch,const,int,ll,贡献,CF1715C,define From: https://www.cnblogs.com/Willette/p/17065636.html