SDNU2020寒假训练赛2题解+补题

目录

    • E.Transformation
    • I*Travel
    • K*Best Cow Fences
    • M.Fight Against Monsters

题目链接

E.Transformation

题意:长度为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));
		}
	}
}

I*Travel

题意:给定一个完全图,其中有两种边,长度为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]));
		}
	}
}	

K*Best Cow Fences

题意:给定一个正整数数列(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));
}

M.Fight Against Monsters

题意:现在有 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);
	}
}

你可能感兴趣的:(Solutions,for,Contests)