首页 > 其他分享 >【题解】[Ynoi2015] 我回来了

【题解】[Ynoi2015] 我回来了

时间:2023-03-08 17:11:56浏览次数:48  
标签:触发 随从 int 题解 可以 Ynoi2015 times 回来 伤害

题目分析:

所谓的期望乘以 \(R - L + 1\) 其实就是求亵渎的触发次数,因为我们能选择的伤害只有 \(R - L + 1\) 种。
有一个很显然的转化,就是对于伤害 \(d\),我们以随从的血量为下标,将这个序列划分为 \([1,d],[d+1,2\times d],\cdots,[d\times \lfloor \frac{n}{d} \rfloor+1,n]\)。
而我们只会有 \(O(n \log n)\) 个划分出来的区间,就是一个调和级数。
可以发现若对于前 \(i\) 段,使得每一段的血量里都至少有一个随从满足条件,那么伤害 \(d\) 就可以触发 \(i\) 次亵渎。因为这个题不强制在线,所以只要我们可以预处理出来 \(g_{d,i}\) 表示伤害 \(d\) 能触发的亵渎次数为 \(i\) 的最早时间(这里的时间就是指的操作顺序),那么只要在 \(g_{d,i}\) 之后伤害 \(d\) 都会至少触发 \(i\) 次。
很显然,只要我们可以预处理出这个东西,就一定可以解决这个问题。
设 \(t_{d,i}\) 表示伤害 \(d\) 的第 \(i\) 段存在一个随从的最早时间,那么就有:

\[g_{d,i} = \max(g_{d,i-1},t_{d,i}) \]

那么 \(t_{d,i}\) 就很简单了,设 \(a_i\) 表示血量为 \(i\) 的随从的最早出现时间,则:

\[t_{d,i} = \min_{d \times (i-1)+1\le x \le d \times i} a_x \]

直接一个 \(st\) 表就好了,那么我们就可以快速求出 \(g_{d,i}\) 了,那么考虑怎么用 \(g_{d,i}\) 去解决问题呢。
其实是很简单的,就是当我们每一次达到某个 \(g_{d,i}\) 的值的时候就将伤害 \(d\) 产生的贡献加 \(1\),这样到第 \(i\) 个的时候就可以使得贡献加 \(i\) 了,这个显然就可以使用树状数组维护了。当然我们也可以每次到达某一个 \(g_{d,i}\) 就将伤害 \(d\) 的贡献设为 \(i\),这样也是可以的。
因为我们是要按照时间顺序遍历,所以每一次都全访问一遍 \(g_{d,i}\) 就很不现实,所以就可以考虑把 \(g_{d,i}\) 放到桶里,这样就可以快速知道当前时间有哪些是需要处理的了。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =  2e6+5;
int n,m,sum[N],f[N][22],a[N];
pair<int,int> q[N];
vector<int> v[N];
void modify(int x){
	for(int i=x; i<=n; i+=i&(-i))	sum[i]++;
}
int query(int x){
	int ans = 0;
	for(int i=x;i;i-=i&(-i))	ans += sum[i];
	return ans;
}
int Query(int l,int r){
	int len = log2(r-l+1);
	return min(f[l][len],f[r - (1<<len) + 1][len]);
}
signed main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	scanf("%lld%lld",&n,&m);
	for(int i=1; i<=n; i++)	a[i] = m + 1;
	for(int i=1; i<=m; i++){
		int opt;scanf("%lld",&opt);
		q[i] = {-1,-1};
		if(opt == 1){
			int x;scanf("%lld",&x);
			a[x] = min(a[x],i);
		}
		else{
			int l,r;scanf("%lld%lld",&l,&r);
			q[i].first = l,q[i].second = r;
		}
	}
	for(int i=1; i<=n; i++)	f[i][0] = a[i];
	for(int i=1; i<=20; i++){
		for(int j=1; j + (1<<i) - 1 <=n; j++){
			f[j][i] = min(f[j][i-1],f[j+(1<<(i-1))][i-1]); 
		}
	}
	for(int d=1; d<=n; d++){   //枚举 d 
		modify(d);
		int lst = 0;
		for(int j=1; j<=n; j+=d){
			lst = max(lst,Query(j,min(j+d-1,n)));
			v[lst].push_back(d);
		}
	}
	for(int i=1; i<=m; i++){
		for(auto x : v[i])	modify(x);
		if(q[i].first != -1){
			int l = q[i].first,r = q[i].second;
			printf("%lld\n",query(r) - query(l-1));
		}
	}
	return 0;
}

标签:触发,随从,int,题解,可以,Ynoi2015,times,回来,伤害
From: https://www.cnblogs.com/linyihdfj/p/17192602.html

相关文章

  • DP练习1题解
    这是2023.3.7DP题单十个题的题解。T1ColoredSubgraphs(CF1796E)简要题意:给定一棵树,你要对树进行链剖分,必须剖成上下竖直的链,求剖后最短链的长度最大值。最小值最大......
  • 【题解】[Ynoi2013] 大学
    题目分析:考虑对于一次修改至少会让\(x\)变成\(\frac{x}{2}\)所以对于每一个数最多被操作\(\log{n}\)次,那么直接暴力操作就好了。所以其实关键问题是每次怎么找到哪......
  • QOJ5256 [CERC2022] H. Insertions 题解
    题面题意:给定字符串\(S,T,P\),求将\(T\)插入进\(S\)之后\(P\)最多的出现次数。输出:最多的出现次数;达到这个最多出现次数的插入位置数量;达到这个最多出现次数......
  • 【题解】[Ynoi2010] y-fast trie
    题目分析:显然可以对于所有的\(x\)对\(C\)取模后处理。那么就是两种情况:\(x+y\geC\)\(x+y<C\)对于第一种情况直接找最大的两个数就好了,关键就是第二种情......
  • 春测2023 T2题解
    这是一个从暴力到正解的过程。暴力从\(1\)枚举到\(\sqrtn\),枚举每一个\(x\),进行累乘,顺便用一个map数组判重,时间复杂度\(\mathcal{O}(\sqrtn\logn\logn)\),直接T飞......
  • Ubuntu服务器ssh缓慢问题解决
    Ubuntu服务器ssh缓慢问题解决现象:ssh登陆或su-账号时间较长 排查:1、查看了/var/log/syslog未发现明显报错2、查看/var/log/auth.log发现有pam_systemd:Failedtocreate......
  • AGC060C题解
    Link简要题意:称一个长为\(2^n-1\)的排列\(P\)像堆,如果\(P_i\ltP_{2i}\),且\(P_i\ltP_{2i+1}\)。给定\(a,b\),设\(u=2^a,v=2^{b+1}-1\),在所有像堆的排列中任取......
  • ABC279H 题解
    学考时候推的。场上记错模数,以为是\(998244353\)然后想了半天组合数怎么算)简单题。首先序列问题考虑每个位置的生成函数然后乘起来。位置\(k\)的生成函数显然是\[\s......
  • 指针8道笔试题解析
    笔试题1:intmain(){inta[5]={1,2,3,4,5};int*ptr=(int*)(&a+1);printf("%d,%d",*(a+1),*(ptr-1));return0;}//程序的结果是什么?第一......
  • P3574 [POI2014] FAR-FarmCraft 吐槽 + 题解
    洛谷上面的题解写的真的不太好,有很多错误,我来谈谈自己的理解。设\(f[i]\)表示以\(i\)为根节点的子树中(包括节点\(i\))的所有人安装好游戏所需要的时间(与下面的\(g[i]......