正题
第一题:区间和2
给出一个序列A,把所有子区间的和提出来后排序,询问的和。
首先考虑差分,然后二分那个值是多少,check用Two_Pointers,直接用一个二分前缀和计算答案即可。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int T;
int n,q;
long long a[200010],f[200010],g[200010];
long long get_tot(int x){
int tot=0;
long long ans=0;
for(int l=1,r=0;l<=n;tot-=a[l],l++){
while(r<n && tot+a[r+1]<=x) tot+=a[++r];
if(r>=l) ans+=g[r]-g[l-1]-f[l-1]*(r-l+1);
}
return ans;
}
long long check(int x){
int tot=0;
long long ans=0;
for(int l=1,r=0;l<=n;tot-=a[l],l++){
while(r<n && tot+a[r+1]<=x) tot+=a[++r];
if(r>=l) ans+=r-l+1;
}
return ans;
}
long long get_sum(long long x){
int l=0,r=f[n],ans=0;
long long temp=0,a=0;
while(l<=r){
int mid=(l+r)/2;
if((temp=check(mid))>=x) r=(ans=mid)-1,a=temp;
else l=mid+1;
}
return get_tot(ans)-(a-x)*ans;
}
int main(){
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
scanf("%d",&T);
long long l,r;
while(T--){
scanf("%d %d",&n,&q);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),f[i]=f[i-1]+a[i],g[i]=g[i-1]+f[i];
while(q--){
scanf("%lld %lld",&l,&r);
printf("%lld\n",get_sum(r)-get_sum(l-1));
}
}
}
第二题:安全路径
我也不知道为什么一堆大神卡在这题上了。给一棵 个点的树,边的颜色非蓝即红。对于树上的一条路径,如果该路径上的所有边均为蓝色,则这 条路径是危险的,否则是安全的。 你想从树上挑出三个点,使得它们任意两点之间的路径都是安全的。
问有多少种选择方案。
我们把蓝边看成0边,红边看成1边,那么一个合法的方案两两路径的或值都为1。
考虑补集转化。一个不合法的方案一定存在一条路径的或值为0,那么说明这两个点在一个0连通块里面,对于每一个0连通块计算答案即可。因为没加ll丢掉许多分。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int n;
struct edge{
int y,next;
}s[100010];
int first[50010],len=0;
bool vis[50010];
long long ans=0;
const long long mod=1e9+7;
void ins(int x,int y){
s[++len]=(edge){y,first[x]};first[x]=len;
s[++len]=(edge){x,first[y]};first[y]=len;
}
int dfs(int x,int fa){
int size=1;vis[x]=true;
for(int i=first[x];i!=0;i=s[i].next) if(s[i].y!=fa) size+=dfs(s[i].y,x);
return size;
}
int main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
scanf("%d",&n);
int x,y;
char c[2];
for(int i=1;i<n;i++){
scanf("%d %d",&x,&y);
scanf("%s",c);
if(c[0]=='b') ins(x,y);
}
for(int i=1;i<=n;i++) if(!vis[i]){
int size=dfs(i,0);
if(size==1) continue;
(ans+=1ll*size*(size-1)/2*(n-size)+1ll*size*(size-1)/2*(size-2)/3)%=mod;
}
printf("%lld\n",(1ll*n*(n-1)/2*(n-2)/3-ans)%mod);
}
第三题:小朋友的笑话
小O是一个很萌很萌的女孩子。 有一天小O叫了很多很多萌萌哒小朋友到家里来玩。 由于太无聊了,她们开始讲笑话。 总共有N个小朋友排成一排,编号1-N。 在某个时刻,会有编号为x_i的小朋友看到了笑话 ,然后她会把这个笑话讲出来,与她距离不超过 的 小朋友都会听到这个笑话。 当一个小朋友听到一个笑话时,如果她是第一次听得到这个笑话,那么她会觉得这个笑话非常好笑,笑 的停不下来。如果她听到之前就是在笑的她会继续笑。 如果她之前听过这个笑话,那么她会觉得这个笑话非常无聊,她会立即停止笑。 现在小O想知道,在某些时刻一段区间内有多少小朋友在笑。
想到每一次只会有听过的会停止笑,没有听过的就会开始笑。
我们维护一个线段树,存的是节点是否在笑。
每次讲笑话的时候,我们先强制让这个区间内全部的人笑,然后对于每一个笑话维护一个set,找到与当前区间有交的区间,让那些听过笑话的停下来就好了。同时合并区间插回去。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<set>
#define ls now<<1
#define rs now<<1|1
using namespace std;
int n,m;
struct node{
int l,r;
bool operator<(const node q)const{
return r!=q.r?r<q.r:l<q.l;
}
};
set<node> S[100010];
set<node>::iterator it;
int lazy[300010],tot[300010];
void pushdown(int now,int l,int r){
if(!lazy[now]) return ;
int mid=(l+r)/2;
if(lazy[now]==1) tot[ls]=mid-l+1,tot[rs]=r-mid;
else tot[ls]=tot[rs]=0;
lazy[ls]=lazy[rs]=lazy[now];lazy[now]=0;
}
void change(int now,int x,int y,int l,int r,int t){
if(x==l && y==r){
if(t==0) lazy[now]=2,tot[now]=0;
if(t==1) tot[now]=r-l+1,lazy[now]=1;
return ;
}
pushdown(now,l,r);
int mid=(l+r)/2;
if(y<=mid) change(ls,x,y,l,mid,t);
else if(mid<x) change(rs,x,y,mid+1,r,t);
else change(ls,x,mid,l,mid,t),change(rs,mid+1,y,mid+1,r,t);
tot[now]=tot[ls]+tot[rs];
}
int get_sum(int now,int x,int y,int l,int r){
if(x==l && y==r) return tot[now];
pushdown(now,l,r);
int mid=(l+r)/2;
if(y<=mid) return get_sum(ls,x,y,l,mid);
else if(mid<x) return get_sum(rs,x,y,mid+1,r);
else return get_sum(ls,x,mid,l,mid)+get_sum(rs,mid+1,y,mid+1,r);
}
int main(){
scanf("%d %d",&n,&m);
int op,x,l,k,r,ll,rr;
while(m--){
scanf("%d %d %d",&op,&x,&k);
if(op==1){
scanf("%d",&l);
r=min(n,x+l);l=max(1,x-l);ll=l,rr=r;
change(1,l,r,1,n,1);
while(1){
it=S[k].lower_bound((node){0,ll});
if(it==S[k].end() || (*it).l>rr) break;
node X=*it;
change(1,max(ll,X.l),min(rr,X.r),1,n,0);
l=min(l,X.l),r=max(r,X.r);S[k].erase(it);
}
S[k].insert((node){l,r});
}
else printf("%d\n",get_sum(1,x,k,1,n));
}
}
第四题:我还是不太懂期望那一套理论,学懂了再回来补。
本文精选四道算法竞赛题目,包括区间和问题、安全路径、小朋友的笑话及一个待研究的期望理论问题,通过深入解析提供高效解决方案。文章涵盖了差分、二分查找、线段树、动态规划等算法技巧,适合算法爱好者和竞赛选手学习。
817

被折叠的 条评论
为什么被折叠?



