SDNU2020寒假訓練賽2題解+補題

題目鏈接

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<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
#include<string.h>
#include<queue>
#include<vector>
using namespace std;
typedef long long LL;
#define IT set<node>::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<iostream>
#include<stdio.h>
#include<string.h>
#include<map>
#include<algorithm>
#include<queue>
#include<math.h>
#include<set>
#include<vector>
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<iostream>
#include<stdio.h>
#include<string.h>
#include<map>
#include<algorithm>
#include<queue>
#include<math.h>
#include<vector>
#include<set>
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<iostream>
#include<stdio.h>
#include<string.h>
#include<map>
#include<algorithm>
#include<queue>
#include<math.h>
#include<set>
#include<vector>
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<iostream>
#include<stdio.h>
#include<string.h>
#include<map>
#include<algorithm>
#include<queue>
#include<math.h>
#include<vector>
#include<cstring>
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);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章