zoukankan      html  css  js  c++  java
  • 【洛谷P3889】吃

    题目

    题目链接:https://www.luogu.com.cn/problem/P3889
    W师兄计划了很久,终于成功的在BG开了一家寿司店。
    正当W师兄还在兴奋的时候,这时一个噩耗传来,吃货L师姐居然知道了这件事,而且正赶过来,W师兄瞬间心就冷了下去,但是机智的W师兄也瞬间想到了应付L师姐的策略.......
    这时,L师姐到了寿司店,先四处望了望风景,发现现在只有L师姐一个顾客,下面是L师姐的选餐说明:

    1. 寿司店内的寿司被排在一行共N个盘子里,按从左到右编号为\(1\sim N\)
    2. 每个位置上寿司的数量是确定的并且有玻璃窗保护。
    3. 每隔一段时间就会有一个选餐时间,L师姐可以在一个连续的区间\([l,r]\)中选择其中一盘,然后在该区间之外选择另一盘(如果区间外有盘子)。

    L师姐发现这家寿司店厨师的制作速度很快,总能在下一次选餐时间前将寿司数量恢复原样。
    作为有尊严有追求的吃货,L师姐也有自己的规则,L师姐在选完两盘寿司后,会决定每口恰好吃\(D\)个寿司,且使得两盘寿司刚好可以分别吃完,不剩余任何寿司。比如两盘寿司数量为2和4,那么\(D=1\)或者\(D=2\)都可以恰好将两盘寿司分别吃干净,而两盘寿司数量为3和5时,那么只能\(D=1\)才行。
    作为有特殊追求的L师姐才不在乎吃的数量,L师姐在乎的是一口吃多个寿司的感觉。于是,如果L师姐可以一口吃\(D\)个寿司,那么L师姐的愉悦值为\(D\),但是L师姐没有选到两盘寿司,那么她的愉悦值为0。
    现在L师姐知道每个盘子所放着的寿司数量,L师姐想知道每次选择时间过后她可以获得的最大愉悦值是多少?

    思路

    等价于每次给出一个区间\([l,r]\),在\([l,r]\)中取一个数\(x\),在\([1,l)∪(r,n]\)中取一个数\(y\),使得\(gcd(x,y)最大\)

    考虑将每次询问拆分成\([1,l)\)\((r,n]\)两部分分别取一个数\(y\),然后在再\(gcd(x,y_1)\)\(gcd(x,y_2)\)中取一个最大值,这样就只要做半(???)个问题。
    记录每一个数的因子\(fac[x][...]\),考虑离线解决。
    将询问以\(l\)为关键字从小到大排序,那么从一个询问\([l_1,r_1]\)转移到\([l_2,r_2](l_1\leq l_2)\)时,我们只需要处理出\([l_1,l_2)\)对后面的贡献。
    对于\(x\in [l_1,l_2)\),假设\(x\)的一个因子为\(p\),在\(x\)的后面还有若干个数字\(x_1,x_2...\)也含有因子\(p\)。其实我们只关心距离\(x\)最近的一个数字\(x_1\),记录对它的贡献即可。因为对于\(x_2\)的贡献可以在枚举到\(x_1\)时计算。
    开一棵线段树,在离线处理每一个询问时,线段树一个节点\([l,r]\)表示\([l,r]\)中的数字与离线处理到的位置前的数字的最大\(gcd\)(我在写什么啊
    所以我们设\(nxt[x][i]\)表示序列中下一个含有\(fac[x][i]\)的数的位置。那么当我们枚举到数字\(x\)时,我们便利它的每一个因子,将线段树中\(nxt[x][i]\)\(maxgcd\)在原来的\(maxgcd\)\(fac[x][i]\)中取最大值。然后对于这个询问的答案就是询问区间的\(maxgcd\)
    这样就搞定了半个问题,对于另外半个问题,我们把序列、因数、询问等全部颠倒过来做一遍就可以了。然后在两个答案中取\(max\)
    数字\(x\)的因数上限是\(2\sqrt{x}\),如果直接开这么大的数组空间会炸,考虑到很多数字的因数个数远远达不到上限,\(nxt\)\(fac\)就用\(vector\)省空间即可。
    时间复杂度\(O(n\log n+n\sqrt{n})\)
    我自己都不知道在写什么,看代码吧(

    代码

    #include <cstdio>
    #include <cctype>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N=100010;
    int n,m,a[N],ans[N],last[N];
    vector<int> fac[N],nxt[N];
    
    inline int read()
    {
    	int d=0; char ch=getchar();
    	while (!isdigit(ch)) ch=getchar();
    	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
    	return d;
    }
    
    struct ASK
    {
    	int l,r,id;
    }ask[N];
    
    struct Treenode
    {
    	int l,r,maxgcd;
    };
    
    struct TREE
    {
    	Treenode tree[N*4];
    	
    	inline void build(int x,int l,int r)
    	{
    		tree[x].l=l; tree[x].r=r; tree[x].maxgcd=0;
    		if (l==r) return;
    		int mid=(l+r)>>1;
    		build(x*2,l,mid);
    		build(x*2+1,mid+1,r);
    	}
    	
    	inline void update(int x,int k,int val)
    	{
    		if (tree[x].l==k && tree[x].r==k)
    		{
    			tree[x].maxgcd=max(tree[x].maxgcd,val);
    			return;
    		}
    		int mid=(tree[x].l+tree[x].r)>>1;
    		if (k<=mid) update(x*2,k,val);
    			else update(x*2+1,k,val);
    		tree[x].maxgcd=max(tree[x*2].maxgcd,tree[x*2+1].maxgcd);
    	}
    	
    	inline int ask(int x,int l,int r)
    	{
    		if (tree[x].l==l && tree[x].r==r)
    			return tree[x].maxgcd;
    		int mid=(tree[x].l+tree[x].r)>>1;
    		if (r<=mid) return ask(x*2,l,r);
    		else if (l>mid) return ask(x*2+1,l,r);
    		else return max(ask(x*2,l,mid),ask(x*2+1,mid+1,r));
    	}
    }Tree;
    
    inline bool cmp(ASK x,ASK y)
    {
    	return x.l<y.l;
    }
    
    inline void solve()
    {
    	for (register int i=1;i<=n;i++)
    		nxt[i].clear();
    	Tree.build(1,1,n);
    	memset(last,0,sizeof(last));
    	for (register int i=n;i>=1;i--)
    		for (register int j=0;j<fac[i].size();j++)
    		{
    			nxt[i].push_back(last[fac[i][j]]);
    			last[fac[i][j]]=i;
    		}
    	sort(ask+1,ask+1+m,cmp);
    	for (register int i=1,j=1;i<=m;i++)
    	{
    		for (;j<ask[i].l;j++)
    			for (register int k=0;k<nxt[j].size();k++)
    				if (nxt[j][k]) Tree.update(1,nxt[j][k],fac[j][k]);
    		ans[ask[i].id]=max(ans[ask[i].id],Tree.ask(1,ask[i].l,ask[i].r));
    	}
    }
    
    int main()
    {
    	n=read();
    	for (register int i=1;i<=n;i++)
    	{
    		a[i]=read();
    		for (register int j=1;j*j<=a[i];j++)
    			if (!(a[i]%j))
    			{
    				fac[i].push_back(j);
    				if (a[i]/j!=j) fac[i].push_back(a[i]/j);
    			}
    	}
    	m=read();
    	for (register int i=1;i<=m;i++)
    	{
    		ask[i].l=read(); ask[i].r=read();
    		ask[i].id=i;
    	}
    	solve();
    	for (register int i=1;i<=n/2;i++)  //颠倒
    	{
    		swap(a[i],a[n-i+1]);
    		fac[i].swap(fac[n-i+1]);
    	}
    	for (register int i=1;i<=m;i++)
    	{
    		swap(ask[i].l,ask[i].r);
    		ask[i].l=n-ask[i].l+1;
    		ask[i].r=n-ask[i].r+1;
    	}
    	solve();
    	for (register int i=1;i<=m;i++)
    		printf("%d\n",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    WPF Image控件的Source属性是一个ImageSource对象
    wx:if 与hidden
    切换远程分支
    异步请求(简单一说)
    多维数组降维方法,简单一提
    3.25发版之最后的搜索框
    wepy-城市按字母排序
    new一个新对象。。。对象???
    参数函数是对象的理解
    群辉 MariaDB 10 远程连接
  • 原文地址:https://www.cnblogs.com/stoorz/p/12215316.html
Copyright © 2011-2022 走看看