usaco 奶牛集会 && 奶牛抗议
奶牛集会
Description
约翰家的N头奶牛每年都会参加“哞哞大会” 。哞哞大会是世界奶牛界的盛事。集会上 的活动很多,比如堆干草,跨栅栏,摸牛仔的屁股等等。当然,哞哞大叫肯定也包括在内。 奶牛们的叫声很大,很多奶牛在参加多年活动之后,实际上已经失去了一部分的听力。
奶牛们已经站在了一条直线上,i号奶牛的坐标为Xi,她只能听到大于Vi的声音。每头奶 牛的位置坐标都是唯一的。
如果i号和j号奶牛想对话,则他们使用的音量为max {Vi, Vj} × |Xi −Xj|
N头奶牛可以组合成N(N − 1)/2对奶牛,假设每对奶牛都使用最小的音量在对话,请计 算所有奶牛产生的总音量是多少。
Input Format
第一行:单个整数:N,1 ≤ N ≤ 20000
第二行到第N + 1行:每行有两个用空格分开的整数,Vi和Xi,1 ≤ Vi ≤ 20000, 1 ≤ Xi ≤ 20000
Output Format
第一行:单个整数,表示最小音量之和
----------------------------------------------------------
正解=线段树(其实用树状数组也可以)
注意到max{vi,vj}中,显然只有大的才有贡献
将 vi 从小到大排序一一进树:
设当前做到 i
先统计当前线段树中比xi大的个数num1及和sum1,ans+=vi*(sum1-num1*xi)
再统计当前线段树中比xi小的个数num2及和sum2,ans+=vi*(num2*xi-sum2)
将 xi 插入线段树
Ps.好像很都存在两个相对大小变量的题,按其中一个变量排序后,题目都会变得很愉悦~
代码如下:
1 #include<cstring> 2 #include<algorithm> 3 #include<cstdio> 4 #include<string> 5 #include<iostream> 6 #define INF 9999999 7 #define LL long long 8 using namespace std; 9 struct Point{ 10 int x; 11 long long v; 12 }a[20001]; 13 bool cmp(const Point&X,const Point&Y){ 14 return X.v<Y.v; 15 } 16 int Min=INF,Max=-INF; 17 int L,R,o,n; 18 long long sum[200000],num[200000]; 19 long long now,ans; 20 long long query(int root,int l,int r){ 21 if(!num[root]) return 0; 22 if(L<=l&&r<=R) 23 return sum[root]-num[root]*now; 24 int mid=(l+r)>>1; 25 long long t=0; 26 if(mid>=L) t+=query(root<<1 ,l ,mid); 27 if(mid<R) t+=query(root<<1|1,mid+1,r); 28 return t; 29 } 30 void insert(int root,int l,int r,int p){ 31 sum[root]+=p; 32 num[root]++; 33 if(l==r) return ; 34 int mid=(l+r)>>1; 35 if(mid>=p) insert(root<<1,l,mid,p); 36 else insert(root<<1|1,mid+1,r,p); 37 } 38 int main(){ 39 scanf("%d",&n); 40 for(int i=1;i<=n;i++){ 41 scanf("%lld%d",&a[i].v,&a[i].x); 42 Min=min(a[i].x,Min); 43 Max=max(a[i].x,Max); 44 } 45 sort(a+1,a+1+n,cmp); 46 LL T=0; 47 for(int i=1;i<=n;i++){ 48 now=a[i].x; 49 L=a[i].x ; R=Max; 50 ans+=a[i].v*query(1,Min,Max); 51 L=Min ; R=a[i].x; 52 ans-=a[i].v*query(1,Min,Max); 53 insert(1,Min,Max,a[i].x); 54 } 55 printf("%lld",ans); 56 }
-----------------------------------------------------------------------------------
奶牛抗议
Description
约翰家的N头奶牛聚集在一起,排成一列,正在进行一项抗议活动。第i头奶牛的理智度 为Ai,Ai可能是负数。约翰希望奶牛在抗议时保持理性,为此,他打算将所有的奶牛隔离成 若干个小组,每个小组内的奶牛的理智度总和都要大于零。由于奶牛是按直线排列的,所以 一个小组内的奶牛位置必须是连续的。
请帮助约翰计算一下,存在多少种不同的分组的方案。由于答案可能很大,只要输出答 案除以1,000,000,009的余数即可。
Input Format
第一行:单个整数:N,1 ≤ N ≤ 10^6
第二行到N + 1行:在第i + 1行有一个整数:Ai,表示第i头奶牛的理智度,−10^5 ≤ Ai ≤ 10^5
Output Format
第一行:单个整数,表示分组方案数除以1,000,000,009的余数
--------------------------------------------------------------------------------------------------
正解=离散化+树状数组,
和上一题有异曲同工之妙~
设f[i] 为到第 i 只奶牛有几种分组,
设sum[i]为奶牛的前缀和
显然 f[i]= ∑f[j](sum[i]-sum[j]>=0)
注意到条件可以转化为 sum[i]>=sum[j],
f[i]=在 i 前 sum[j] 小于 sum[j] 的 f[i] 的和
可以用树状数组求和(由于只有单点修改,用树状数组比较方便 >_<)
sum中显然有许多无用间隔,离散之,
代码如下:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<string> 5 #include<cmath> 6 #include<queue> 7 #include<iostream> 8 #define INF 999999999999 9 #define LL long long 10 #define N 1000002 11 #define key 1000000009 12 //using namespace std ; 13 struct Cow{ 14 LL sum; 15 int p; 16 }a[N]; 17 bool cmp(const Cow &x,const Cow &y){ 18 return x.sum<y.sum; 19 } 20 int L,R,P,n,p[N]; 21 LL sum[N]; 22 int lowbit(int x){ 23 return x&(-x); 24 } 25 void update(int x,LL val){ 26 while(x<=n){ 27 sum[x]=(sum[x]+val)%key; 28 x+=lowbit(x); 29 } 30 } 31 LL cal(int x){ 32 LL temp=0; 33 while(x){ 34 temp=(temp+sum[x])%key; 35 x-=lowbit(x); 36 } 37 return temp; 38 } 39 int main(){ 40 scanf("%d",&n); 41 for(int i=1;i<=n;i++){ 42 LL val; 43 scanf("%I64d",&val); 44 a[i].sum=a[i-1].sum+val; 45 a[i].p=i; 46 } 47 a[n+1].sum=0; 48 a[n+1].p=n+1; 49 std :: sort(a+1,a+n+2,cmp); 50 int t=0; 51 for(int i=1;i<=n+1;i++){ 52 if(i==1||a[i].sum!=a[i-1].sum) ++t; 53 p[a[i].p]=t; 54 } 55 update(p[n+1],1); 56 LL temp=0; 57 for(int i=1;i<=n;i++){ 58 temp=cal(p[i]); 59 update(p[i],temp); 60 } 61 printf("%I64d",temp); 62 }