11.13解题报告

T1 pineapple

题目描述

《暗黑破坏神》是一款经典的 \(\text{RPG}\) 游戏。其英文名为 \(\text{Diablo}\),因发音相似而被称为“大菠萝”。游戏中每个玩家都有两个属性,被称为速度和韧性。
即墨计算机职业技术学院的办公室里, \(R\) 老师正在玩大菠萝。他开启了一个宝箱,宝箱
里有 \(n\) 件装备。第 \(i\) 件装备的信息如下:

  1. 要穿戴上第 \(i\) 件装备,则人物的速度不能低于 \(a_i\),韧性不能低于 \(b_i\)
  2. 穿戴上第 \(i\) 件装备后,人物的速度会增加 \(c_i\),韧性会增加 \(d_i\)

R 老师想知道,如果他想将这 \(n\) 件装备都穿上,那么他的人物在没有穿戴任何装备前的
初始速度至少是多少?在初始速度最小的前提下,初始韧性至少应是多少?
显然,初始的速度和韧性都应该是非负整数。

输入格式

输入的第一行是一个整数 \(T\),表示数据的组数。接下来对每组数据依次给出输入信息。

每组数据的第一行是一个整数,表示装备的数量 \(n\)
接下来 \(n\) 行,每行四个整数,依次表示一件装备的四个属性 \(a_i, b_i, c_i, d_i\)

输出格式

对每组数据输出一行两个整数,用空格隔开,分别表示最小的初始速度和初始速度最小的前提下最小的初始韧性。

100分思路:

首先对速度贪心,排个序从小到大取算出初始答案。
然后对于每一次取完装备后,在满足当前速度的范围内,在线段树上找最小的韧性,当然也可以用堆。

int T, n;
struct node {
	ll a, b, c, d;
}sz[100005];
bool cmp1(node x, node y) {
	return x.a<y.a;
}
struct tree {
	int l, r, minb;
}t[400005];
void push_up(int g) {
	if(sz[t[g<<1].minb].b<=sz[t[g<<1|1].minb].b)
	t[g].minb=t[g<<1].minb;
	else 
	t[g].minb=t[g<<1|1].minb;
}
void build(int g, int l, int r) {
	t[g].l=l; t[g].r=r;
	if(l==r) {
		t[g].minb=l;
		return ;
	} int mid=l+r>>1;
	build(g<<1, l, mid); build(g<<1|1, mid+1, r);
	push_up(g);
}
ll A, B, sum;
int Min(int g, int l, int r) {
	if(l<=t[g].l&&t[g].r<=r) return t[g].minb;
	int mid=t[g].l+t[g].r>>1;
	int re=0;
	if(l<=mid) re=Min(g<<1, l, r);
	if(r>mid) {
		int k=Min(g<<1|1, l, r);
		if(re==0) re=k;
		else if(sz[k].b<sz[re].b) re=k;
	}
	return re;
} 
void Change(int g, int x) {
	if(t[g].l==t[g].r) {
		A+=sz[x].c;
		if(sz[x].b<=sum) sum+=sz[x].d;
		else B+=sz[x].b-sum, sum=sz[x].b+sz[x].d;
		t[g].minb=0;
		return ;
	} int mid=t[g].l+t[g].r>>1;
	if(x<=mid) Change(g<<1, x);
	else Change(g<<1|1, x);
	push_up(g);
} 
int main(){
	freopen("pineapple.in", "r", stdin);
	freopen("pineapple.out", "w", stdout);
	sz[0].b=2e9;
	T=read();
	while(T--) {
		n=read();
		for(int i=1; i<=n; ++i) {
			sz[i].a=read(); sz[i].b=read(); sz[i].c=read(); sz[i].d=read();
		}
		sort(sz+1, sz+n+1, cmp1);
		A=0, sum=0;
		for(int i=1; i<=n; ++i) {
			if(sz[i].a<=sum) {
				sum+=sz[i].c;
			}
			else {
				A+=sz[i].a-sum;
				sum=sz[i].a+sz[i].c;
			}
		}
		cout<<A; putchar(' ');
		int R=1; sum=0; B=0;
		build(1, 1, n);
		for(int i=1; i<=n; ++i) {
			while(R<n&&A>=sz[R+1].a) ++R;
			int x=Min(1, 1, R);
			Change(1, x);
		}
		cout<<B; putchar('\n');
	}
}

T2 pinball

题目:

即墨计算机职业技术学院有一台古老的游戏机,上面有一款比三维弹球还要古老的游戏,被称为“二维弹球”。

游戏的界面是一个平面,有 \(n\) 个“弹球点”围成一圈,他们从 \(1\)\(n\) 编号。初始时,弹球在 \(1\) 号弹球点处。弹球所在的弹球点可以把球弹到除了它自身以外的任何一个弹球点,称为一次传球。

由于机器年久失修,有 \(m\) 条传球路径已经不可用,其中第 \(i\) 条不能使用的路径形如 \((x_i, y_i)\),表示 \(x_i\) 号弹球点不能弹到 \(y_i\) 号弹球点,注意,这并不代表 \(y_i\) 号弹球点不能弹到 \(x_i\) 号弹球点。

现在,R 老师想知道,经过 \(k\) 次传球以后,球被弹回 \(1\) 号弹球点的方案数。因为结果可能很大,请你输出这个结果对 \(998244353\) 取模的结果。

100分思路:

首先,如果没有不可用的路径,这显然就是一个简单的 \(dp\)
考虑上不可用路径,那就把不合法的转移减掉就行了。唯一的弊端就是这些转移的复杂度和 \(n\) 有关。
如果不考虑不合法路径,那么简化一下式子可以发现就是简单的上一个总方案数 \(S\) 乘上 \(n-1\)
减掉不合法路径的时候,只需要对这最多 \(10^5\) 个点转移就行了,所以只对这些点开空间就好了。
值域很大需要离散化。

int n, k, m;
struct node {
	int f, t;
}e[50004];
int ls[200005], tt, top;
ll cnt[2][200005];
ll S;
signed main(){
	n=read(); k=read(); m=read();
	ls[++tt]=1;
	for(int i=1; i<=m; ++i) {
		e[i].f=read(); e[i].t=read();
		ls[++tt]=e[i].f; ls[++tt]=e[i].t;
	}
	sort(ls+1, ls+tt+1);
	for(int i=1; i<=tt; ++i) if(ls[i]!=ls[i-1]) ls[++top]=ls[i];
	for(int i=1; i<=m; ++i) {
		e[i].f=lower_bound(ls+1, ls+top+1, e[i].f)-ls;
		e[i].t=lower_bound(ls+1, ls+top+1, e[i].t)-ls;
	}
	for(int i=1; i<=top; ++i) cnt[1][i]=1;
	if(k==0) return putchar('0'), 0;
	cnt[1][1]=0; S=n-1;
	for(int i=1; i<=m; ++i) {
		if(e[i].f!=1) continue ;
		if(e[i].t==1) continue ;
		cnt[1][e[i].t]=0; S--;
	}
	for(int i=2; i<=k; ++i) {
		ll Sum=0;
		Sum=S*(n-1)%MOD;
		for(int j=1; j<=m; ++j) {
			if(e[j].f==e[j].t) continue ;
			cnt[i&1][e[j].f]=S-cnt[i&1^1][e[j].f];
			cnt[i&1][e[j].t]=S-cnt[i&1^1][e[j].t];
			cnt[i&1][e[j].f]=(cnt[i&1][e[j].f]%MOD+MOD)%MOD;
			cnt[i&1][e[j].t]=(cnt[i&1][e[j].t]%MOD+MOD)%MOD;
		}
		cnt[i&1][1]=S-cnt[i&1^1][1];
		cnt[i&1][1]=(cnt[i&1][1]%MOD+MOD)%MOD;
		for(int j=1; j<=m; ++j) {
			if(e[j].f==e[j].t) continue ;
			cnt[i&1][e[j].t]-=cnt[i&1^1][e[j].f];
			cnt[i&1][e[j].t]=(cnt[i&1][e[j].t]%MOD+MOD)%MOD;
			Sum-=cnt[i&1^1][e[j].f];
			Sum=(Sum%MOD+MOD)%MOD;
		}
		S=Sum;
		memset(cnt[i&1^1], 0, sizeof(cnt[i&1^1]));
	}
	cout<<cnt[k&1][1];}

T3 grid

即墨计算机职业技术学院下发了一份实验表格,这份表格共有 \(n\)\(m\) 列,每个格子都是空的。
R 老师非常无聊,于是他从第一行的某个格子起,按照从左往右,从上往下的顺序依次填写从 \(1\) 开始的正整数,直至填满最后一行。
填写完后, R 老师给出了 \(k\) 条信息,每条信息为“整数 \(x_i\)\(y_i\) 在表格中处于同一行”。
现在,你,作为 R 老师的课代表,需要回答他: \(1\) 这个数字被写在了第一行的哪一列?列的标号从 \(1\) 开始。
如果 R 老师给出的信息不能唯一确定这份表格,请你输出“\(He~drank~a~lot!\)”(不含括号);如果不存在任何一个表格符合他给出的所有信息,请输出“\(He~drank~too~much!\)”(不含括号)

60分思路:

直接枚举 \(1\) 的位置,判断所有条件是否合法。

struct node {
	int l, r;
}sz[500005];
int n, m, k;
int tot;
int ans;
signed main(){
	n=read(); m=read(); k=read();
	for(int i=1; i<=k; ++i) {
		sz[i].l=read(); sz[i].r=read();
	}
	for(int i=1; i<=m; ++i) {
		bool flag=1;
		for(int j=1; j<=k; ++j) {
			int a=sz[j].l+i-1;
			int b=sz[j].r+i-1;
			a=(a-1)/m+1; b=(b-1)/m+1;
			if(a!=b) {flag=0; break;}
		}
		if(flag) ans=i, ++tot;
	}
	if(tot==1) cout<<ans;
	if(tot>1) return puts("He drank a lot!"), 0;
	if(tot==0) return puts("He drank too much!"), 0;
}

T4 drink

即墨计算机职业技术学院的封校期间, cym 又一次喝的酩酊大醉。
在大醉中, cym 看到了一个数列,这个数列里的每个数字有三个参数: \(v_i\), \(a_i\), \(b_i\)。这个数列中的数有一个有关实现的性质:每过一个时刻,数列中的每个数的 \(v_i\) 都会增加 \(a_i \times b_i\)
手舞足蹈的 cym 认为他是这个数列的神,于是他大叫着对这个数列进行了一些修改和询问:

  • \(1\) \(t\) \(l\) \(r\) :查询时刻 \(t\) 区间 \([l, r]\) 内数字 \(v_i\) 之和对 \(10^8 + 7\) 取模的结果。
  • \(2\) \(t\) \(l\) \(r\) \(x\):在 \(t\) 时刻给区间 \([l, r]\) 内的数字 \(a_i\) 都加上 \(x\)
  • \(3\) \(t\) \(l\) \(r\) \(x\):在 \(t\) 时刻给区间 \([l, r]\) 内的数字 \(b_i\) 都加上 \(x\)
  • \(4\) \(t\) \(l\) \(r\) \(x\):在 \(t\) 时刻给区间 \([l, r]\) 内的数字 \(v_i\) 都加上 \(x\)

规定初始时刻为 \(0\)
注意,如果时刻 \(t\) 进行了一次对 \(a_i\)\(b_i\) 的修改,那么本次修改对 \(v_i\) 增加的值的影响会在下一时刻生效。即每一个时刻开始时,先计算 \(v\) 的增加,再进行对 \(a_i\), \(b_i\) 的修改。

35分暴力,有 \(5\) 分是用前缀和进行查询操作。

struct node {
	int v, a, b;
}sz[200010];
int n, m;
int T=0, t;
int op, l, r, x;
signed main(){
	freopen("drink.in", "r", stdin);
	freopen("drink.out", "w", stdout);
	n=read(); m=read();
	for(int i=1; i<=n; ++i) {
		sz[i].v=read(); sz[i].a=read(); sz[i].b=read();
	}
	if(n<=10005) {
		while(m--) {
			op=read(); t=read()%MOD; l=read(); r=read();
			for(int i=1; i<=n; ++i) sz[i].v=((sz[i].v+sz[i].a*sz[i].b%MOD*(t-T)%MOD)%MOD+MOD)%MOD;
			if(op==1) {
				int sum=0;
				for(int i=l; i<=r; ++i) sum+=sz[i].v, sum%=MOD;
				cout<<sum; putchar('\n');
			}
			if(op==2) {
				x=read();
				for(int i=l; i<=r; ++i) sz[i].a+=x, sz[i].a%=MOD;
			}
			if(op==3) {
				x=read();
				for(int i=l; i<=r; ++i) sz[i].b+=x, sz[i].b%=MOD;
			}
			if(op==4) {
				x=read();
				for(int i=l; i<=r; ++i) sz[i].v+=x, sz[i].v%=MOD;
			}
			T=t;
		}		
	}
	if(n==200004) {
		for(int i=1; i<=n; ++i) {
			sz[i].v+=sz[i-1].v, sz[i].v%=MOD;
			sz[i].a*=sz[i].b, sz[i].a%=MOD;
			sz[i].a+=sz[i-1].a, sz[i].a%=MOD;
		}
		while(m--) {
			op=read(); t=read(); l=read(); r=read();
			cout<<((sz[r].v-sz[l-1].v+(sz[r].a-sz[l-1].a)*t)%MOD+MOD)%MOD; putchar('\n');
		}
	}
}
posted @ 2022-11-13 21:58  Konnya_ku  阅读(78)  评论(3编辑  收藏  举报