题目链接
题意:长度为n的区间,初始值都是0,四种操作:opt,x,y,z
1.把区间[x,y]每个数加上z
2.把区间[x,y]每个数乘上z
3.把区间[x,y]所有数变成z
4.计算区间[x,y]内所有数的z次方和(这一条的1<=z<=3)
所有操作 mod 10007
思路1:珂朵莉树。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
#define IT set::iterator
struct node{
int l,r;
mutable LL v;
node(int L,int R=-1,LL V=0):l(L),r(R),v(V){}
bool operator<(const node &o)const{
return l<o.l;
}
};
LL qpow(LL a,LL b,LL mod){//快速幂
LL ans=1;
LL x=a%mod;
while(b){
if(b&1)ans=ans*x%mod;
x=x*x%mod;
b>>=1;
}
return ans;
}
set<node>s;
IT split(int pos){
IT it=s.lower_bound(node(pos));
if(it!=s.end()&&it->l==pos)return it;
--it;
int L=it->l,R=it->r;
LL V=it->v;
s.erase(it);
s.insert(node(L,pos-1,V));
return s.insert(node(pos,R,V)).first;
}
void add(int l,int r,LL val){//给l到r所有数加上val
IT itr=split(r+1),itl=split(l);
for(;itl!=itr;++itl)
itl->v=(itl->v+val)%10007;
}
void muti(int l,int r,LL val){//给l到r所有数乘上val
IT itr=split(r+1),itl=split(l);
for(;itl!=itr;++itl)
itl->v=(itl->v*val)%10007;
}
void assign_val(int l,int r,LL val){//把l到r所有数改为val
IT itr=split(r+1),itl=split(l);
s.erase(itl,itr);
s.insert(node(l,r,val));
}
LL sum(int l,int r,int ex){//询问l到r每个数字的x次方和
IT itr=split(r+1),itl=split(l);
LL res=0;
for(;itl!=itr;++itl)
res=(res+(long long)(itl->r - itl->l+1)*qpow(itl->v,(long long)ex,10007))%10007;
return res;
}
int n,m;
int main(){
while(cin>>n>>m,n!=0||m!=0){
s.clear();
for(int i=1;i<=n+1;i++)s.insert(node(i,i,0));
int opt,x,y;
long long z;
for(int i=1;i<=m;i++){
scanf("%d%d%d%lld",&opt,&x,&y,&z);
if(opt==1)add(x,y,z);else
if(opt==2)muti(x,y,z);else
if(opt==3)assign_val(x,y,z);else
printf("%lld\n",sum(x,y,z));
}
}
return 0;
}
思路2:线段树。
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define Inf 0x7fffffff
#define p 10007
typedef long long ll;
const int N=400007;
int n,m;
struct node{
ll s1,s2,s3;
}t[N];
ll lazy[N],lazy2[N],value[N];
void push_up(int k){
t[k].s1=(t[k<<1].s1+t[k<<1|1].s1)%p;
t[k].s2=(t[k<<1].s2+t[k<<1|1].s2)%p;
t[k].s3=(t[k<<1].s3+t[k<<1|1].s3)%p;
}
void push_down(int k,int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
if(value[k]){
value[k<<1]=value[k<<1|1]=value[k];
t[k<<1].s1=((mid-l+1)*value[k])%p;
t[k<<1].s2=((mid-l+1)*value[k]*value[k])%p;
t[k<<1].s3=((mid-l+1)*(value[k]*value[k]*value[k]%p))%p;
t[k<<1|1].s1=((r-mid)*value[k])%p;
t[k<<1|1].s2=((r-mid)*value[k]*value[k])%p;
t[k<<1|1].s3=((r-mid)*(value[k]*value[k]*value[k]%p))%p;
lazy[k<<1]=lazy[k<<1|1]=value[k]=0;
lazy2[k<<1]=lazy2[k<<1|1]=1;
}
if(lazy2[k]!=1){
lazy2[k<<1]=(lazy2[k<<1]*lazy2[k])%p;
lazy2[k<<1|1]=(lazy2[k<<1|1]*lazy2[k])%p;
lazy[k<<1]=(lazy[k<<1]*lazy2[k])%p;
lazy[k<<1|1]=(lazy[k<<1|1]*lazy2[k])%p;
t[k<<1].s3=(t[k<<1].s3*(lazy2[k]*lazy2[k]*lazy2[k]%p))%p;
t[k<<1].s2=(t[k<<1].s2*lazy2[k]*lazy2[k])%p;
t[k<<1].s1=(t[k<<1].s1*lazy2[k])%p;
t[k<<1|1].s3=(t[k<<1|1].s3*(lazy2[k]*lazy2[k]*lazy2[k]%p))%p;
t[k<<1|1].s2=(t[k<<1|1].s2*lazy2[k]*lazy2[k])%p;
t[k<<1|1].s1=(t[k<<1|1].s1*lazy2[k])%p;
lazy2[k]=1;
}
if(lazy[k]){
lazy[k<<1]=(lazy[k<<1]+lazy[k])%p;
lazy[k<<1|1]=(lazy[k<<1|1]+lazy[k])%p;
t[k<<1].s3=(t[k<<1].s3+3*t[k<<1].s2*lazy[k]+3*t[k<<1].s1*lazy[k]*lazy[k]+(mid-l+1)*(lazy[k]*lazy[k]*lazy[k]%p))%p;
t[k<<1].s2=(t[k<<1].s2+2*t[k<<1].s1*lazy[k]+(mid-l+1)*lazy[k]*lazy[k])%p;
t[k<<1].s1=(t[k<<1].s1+(mid-l+1)*lazy[k])%p;
t[k<<1|1].s3=(t[k<<1|1].s3+3*t[k<<1|1].s2*lazy[k]+3*t[k<<1|1].s1*lazy[k]*lazy[k]+(r-mid)*(lazy[k]*lazy[k]*lazy[k]%p))%p;
t[k<<1|1].s2=(t[k<<1|1].s2+2*t[k<<1|1].s1*lazy[k]+(r-mid)*lazy[k]*lazy[k])%p;
t[k<<1|1].s1=(t[k<<1|1].s1+(r-mid)*lazy[k])%p;
lazy[k]=0;
}
}
void build(int l,int r,int k){
t[k].s1=t[k].s2=t[k].s3=lazy[k]=value[k]=0;
lazy2[k]=1;
if(l==r)return;
int mid=(l+r)>>1;
build(l,mid,k<<1);
build(mid+1,r,k<<1|1);
}
void add(int L,int R,int l,int r,int k,ll val){
if(L<=l&&R>=r){
lazy[k]=(lazy[k]+val)%p;
t[k].s3=(t[k].s3+3*t[k].s2*val+3*t[k].s1*val*val+(r-l+1)*(val*val*val%p))%p;
t[k].s2=(t[k].s2+2*t[k].s1*val+(r-l+1)*val*val)%p;
t[k].s1=(t[k].s1+(r-l+1)*val)%p;
return;
}
int mid=(l+r)>>1;
push_down(k,l,r);
if(L<=mid)add(L,R,l,mid,k<<1,val);
if(R>mid)add(L,R,mid+1,r,k<<1|1,val);
push_up(k);
}
void muti(int L,int R,int l,int r,int k,ll val){
if(L<=l&&R>=r){
lazy2[k]=(lazy2[k]*val)%p;
lazy[k]=(lazy[k]*val)%p;
t[k].s3=(t[k].s3*(val*val*val%p))%p;
t[k].s2=(t[k].s2*val*val)%p;
t[k].s1=(t[k].s1*val)%p;
return;
}
int mid=(l+r)>>1;
push_down(k,l,r);
if(L<=mid)muti(L,R,l,mid,k<<1,val);
if(R>mid)muti(L,R,mid+1,r,k<<1|1,val);
push_up(k);
}
void modify(int L,int R,int l,int r,int k,ll val){
if(L<=l&&R>=r){
lazy[k]=0;
lazy2[k]=1;
value[k]=val;
t[k].s1=((r-l+1)*val)%p;
t[k].s2=((r-l+1)*val*val)%p;
t[k].s3=((r-l+1)*(val*val*val%p))%p;
return;
}
int mid=(l+r)>>1;
push_down(k,l,r);
if(L<=mid)modify(L,R,l,mid,k<<1,val);
if(R>mid)modify(L,R,mid+1,r,k<<1|1,val);
push_up(k);
}
ll query(int L,int R,int l,int r,int k,ll e){
if(L<=l&&R>=r){
if(e==1)return t[k].s1;
if(e==2)return t[k].s2;
if(e==3)return t[k].s3;
}
int mid=(l+r)>>1;
push_down(k,l,r);
ll s=0;
if(L<=mid)s+=query(L,R,l,mid,k<<1,e);
if(R>mid)s+=query(L,R,mid+1,r,k<<1|1,e);
return s%p;
}
int main(){
while(scanf("%d%d",&n,&m),n!=0||m!=0){
build(1,n,1);
int opt,x,y;ll z;
for(int i=1;i<=m;i++){
scanf("%d%d%d%lld",&opt,&x,&y,&z);
if(opt==1)add(x,y,1,n,1,z%p);else
if(opt==2)muti(x,y,1,n,1,z%p);else
if(opt==3)modify(x,y,1,n,1,z%p);else
printf("%lld\n",query(x,y,1,n,1,z));
}
}
}
题意:给定一个完全图,其中有两种边,长度为a(给出m<=5e5条)或长度为b(剩下的),求有1~n的最短路径(n<=1e5)
思路:大数据补图(边权相等)
1.若1到n的边为a,则结果为min(a,disb[n]),disb[n]为1到n只走边为b的最小路径。
2.若1到n的边为b,则结果为min(b,disa[n]),disa[n]为1到n只走边为a的最小路径。
对于2直接spfa。对于1,分析可得每个点只入队一次,所以可以用set维护点,做类似于spfa的bfs。
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=200007,M=500007*2;
const ll Inf=1e10;
set<int>st,ts;
int n,m,cnt,head[N],flag;
ll a,b,dis[N],vis[N];
struct edge{int v,next;}e[M];
void init(){
cnt=flag=0;
memset(head,-1,sizeof(head));
}
void add(int x,int y){
e[cnt].v=y;
e[cnt].next=head[x];
head[x]=cnt++;
}
void spfa(){
for(int i=1;i<=n;i++)dis[i]=(i==1?0:Inf),vis[i]=0;
vis[1]=1;
queue<int>q;
q.push(1);
while(!q.empty()){
int now=q.front();q.pop();
for(int i=head[now];i!=-1;i=e[i].next){
if(dis[e[i].v]>dis[now]+a){
dis[e[i].v]=dis[now]+a;
if(!vis[e[i].v]){
q.push(e[i].v);
vis[e[i].v]=1;
}
}
}
}
}
void bfs(){
for(int i=1;i<=n;i++)dis[i]=(i==1?0:Inf);
st.clear();ts.clear();
for(int i=2;i<=n;i++)st.insert(i);
queue<int>q;
q.push(1);
while(!q.empty()){
int now=q.front();q.pop();
for(int i=head[now];i!=-1;i=e[i].next){
if(st.count(e[i].v)==0)continue; //已搜索过这个点
st.erase(e[i].v);ts.insert(e[i].v); //删掉与当前点边权为a的点
}
for(auto &j:st){
q.push(j);
dis[j]=dis[now]+b;
}
st.swap(ts); //恢复删掉的点,继续bfs
ts.clear();
}
}
int main(){
while(~scanf("%d%d%lld%lld",&n,&m,&a,&b)){
init();
int x,y;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
if((x==1&&y==n)||(x==n&&y==1))flag=1;
}
if(flag){
bfs();
printf("%lld\n",min(a,dis[n]));
}else{
spfa();
printf("%lld\n",min(b,dis[n]));
}
}
}
题意:给定一个正整数数列(N<=1e5),求一个平均数最大的,长度不小于 f 的子串,输出最大平均值*1000取整后的结果。
思路:二分答案,因为要使平均值尽可能大。
check(mid) 时,把每个数都减去 mid,则问题转化为了 检查是否存在长度不小于 f 且和不小于 0 的子串
记b[i]=a[i]-mid,sum[i]=b[1]+b[2]+…+b[i]
则只需找到一个区间 [l, r],使得 sum[r]-sum[l -1]>0
那只需记录sum[0]到sum[ i-f ] 的最小值即可在 O(n) 时间复杂度内完成check
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define Inf 0x7fffffff
typedef long long ll;
const int N=100007;
const double eps=1e-5;
int n,f,a[N];
double b[N],min_sum,sum[N];
int check(double mid){
int flag=0;min_sum=0;
for(int i=1;i<=n;i++){
b[i]=a[i]-mid;
sum[i]=sum[i-1]+b[i];
if(i>=f){
min_sum=min(min_sum,sum[i-f]);
if(sum[i]-min_sum>0){flag=1;break;}
}
}
if(flag)return 1;else return 0;
}
int main(){
cin>>n>>f;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
double l=0,r=2000,mid; //这里l要从0开始,题目中的1<=ncows并不是单个田地里牛的数量
while(r-l>eps){
mid=(l+r)/2.0;
if(check(mid))l=mid;else r=mid;
}
printf("%d",(int)(r*1000));
}
题意:现在有 n 只怪兽,每只怪兽有一个体力值 HPi 和一个攻击值 ATKi。英雄需要同时和这 n 只怪兽进行战斗。
在每一秒,首先英雄会被当前未被打倒的所有怪兽攻击,受到与这些怪兽的攻击值之和等量的伤害。然后他要选择一只未被打倒的怪兽进行攻击。对同一只怪物进行的第 i 次攻击能对其造成 i 点伤害。
当怪兽的体力值 ≤ 0 的时候就会倒下,当所有怪兽都被打倒时战斗立即结束。
英雄需要合理地进行攻击以使战斗过程中受到的伤害之和最小,请你求出这个最小伤害总量。
思路:贪心。做法与Protecting the flowers完全一样
设k[i]为打第i只怪兽所需次数
先打怪兽a,再打怪兽b,伤害值为k[a] * (ATK[a]+ATK[b]) + k[b] * ATK[b]
先打怪兽b,再打怪兽a,伤害值为k[b] * (ATK[a]+ATK[b]) + k[a] * ATK[a]
假设第一种方案更优,则
k[a] * (ATK[a]+ATK[b]) + k[b] * ATK[b] < k[b] * (ATK[a]+ATK[b]) + k[a] * ATK[a]
化简得k[a]/ATK[a] < k[b]/ATK[b]
所以按照上式进行排序,得到的序列即为打怪兽的最优顺序
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define Inf 0x7fffffff
typedef long long ll;
const int N=1000007;
int t,n;
struct node{
ll k,at,hp;
bool operator< (const node &o)const{
return (double)k/at<(double)o.k/o.at;
}
}a[N];
ll all,ans;
int main(){
cin>>t;
for(int j=1;j<=t;j++){
cin>>n;
ans=all=0;
for(int i=1;i<=n;i++){
scanf("%lld%lld",&a[i].hp,&a[i].at);
a[i].hp*=2;
a[i].k=sqrt(a[i].hp);
if(a[i].k*(a[i].k+1)<a[i].hp)a[i].k++;
all+=a[i].at;
}
sort(a+1,a+1+n);
for(int i=1;i<=n;i++){
ans+=all*a[i].k;
all-=a[i].at;
}
printf("Case #%d: %lld\n",j,ans);
}
}