NOI Online 2022 提高组

丹钓战

题意:

\(n\)个二元组\((a_i,b_i)\),初始时栈\(S\)为空。

当向其中加入元素\((a_i,b_i)\)时,先不断弹出栈顶元素,直到栈顶元素\((a_j,b_j)\)满足\(a_i\neq a_j,b_i<b_j\)时,再将\((a_i,b_i)\)加入栈中。

如果某个元素入栈后仅有这一个元素在栈中,则称这个元素是成功的。

\(q\)个询问,每个询问形如\([l,r]\),表示询问当把\([l,r]\)的二元组按顺序入栈时,有多少个元素是成功的?

\(n,q\leq 5*10^5\)

题解:

考虑对每个元素预处理一个数组\(pre[i]\),表示该元素入栈时,栈顶的上个元素的标号。

那么询问转化为\([l,r]\)内,有多少个\(k\)满足\(pre[k]<l\)

预处理出\(pre[i]\)数组,然后主席树二维数点。

\(pre[i]\)怎么预处理呢,递推模拟就可以

#include<bits/stdc++.h>
using namespace std;
namespace red{
//#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-8)
    const int N=5e5+10,mod=998244353,inf=2e9;
    int n,m;
    int a[N],b[N];
    int st[N],top;
    int pre[N];
    int rt[N];
    struct zxtree
    {
        int ans[N*20],son[N*20][2];
        int tot=0;
        inline void update(int pos,int l,int r,int pre,int &p)
        {
            p=++tot;
            ans[p]=ans[pre]+1;
            son[p][0]=son[pre][0],son[p][1]=son[pre][1];
            if(l==r) return;
            if(pos<=mid) update(pos,l,mid,son[pre][0],son[p][0]);
            else update(pos,mid+1,r,son[pre][1],son[p][1]);
        }
        inline int query(int tl,int tr,int l,int r,int pre,int p)
        {
            if(tl<=l&&r<=tr) return ans[p]-ans[pre];
            int sum=0;
            if(tl<=mid) sum+=query(tl,tr,l,mid,son[pre][0],son[p][0]);
            if(tr>mid) sum+=query(tl,tr,mid+1,r,son[pre][1],son[p][1]);
            return sum;
        }
    }T;
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        cin>>n>>m;
        for(int i=1;i<=n;++i)
        {
            cin>>a[i];
        }
        for(int i=1;i<=n;++i)
        {
            cin>>b[i];
            while(top>0)
            {
                if(b[st[top]]>b[i]&&a[st[top]]!=a[i]) break;
                --top;
            }
            pre[i]=st[top];
            st[++top]=i;
            T.update(pre[i]+1,1,n+1,rt[i-1],rt[i]);
        }
        for(int i=1;i<=m;++i)
        {
            int l,r;cin>>l>>r;
            cout<<T.query(1,l,1,n+1,rt[l-1],rt[r])<<'\n';
        }

    }
}
signed main()
{
    red::main();
    return 0;   
}
/*
5 5 7 2
5 0 7 2

1 3 1 2 5
1 2 3 1 4

*/

讨论

题意:

\(n\)个人正在打模拟赛,模拟赛有\(n\)道题目。

如果两个人会的题目有交集,并且不存在包含关系,两个人就会讨论。

请你找出一对会讨论的人,或打印不存在。

\(n\leq 10^6\),所有人会的题目总数\(\leq 2*10^6\)

题解:

我们先将人按会的题目数从小到大排序。

对于当前处理到的第\(i\)个人,假如他会题目\(x\)

只要让他和上一个会题目\(x\)的人做一次比较即可。

简略证明:

假如当前人\(i\)会题目\(x\),上一个会题目\(x\)的人是\(j\),再之前会题目\(x\)的人是\(k\)

因为处理到了\(i\),所以说明\(j,k\)不会进行讨论。

\(j\)会的题目更多,说明\(j\)包含\(k\)

那么,如果\(i,j\)不会讨论,说明\(i\)包含\(j\),也就包含了\(k\),没有必要比较。

如果\(i,j\)会讨论,就可以结束比较了耶~

被比较过的人就可以删除了,之后不再理会。

所以总复杂度\(O(m)\)

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
	const int N=1e6+10,inf=2e9;
	int n,m;
	struct node
	{
		vector<int> q;
		int id;
		inline bool operator < (const node &t) const
		{
			return q.size()<t.q.size();
		}
	}a[N];
	bool vis[N],del[N];
	int pre[N];
	inline void init()
	{
		for(int i=1;i<=n;++i)
		{
			a[i].q.clear();
			pre[i]=0;
			del[i]=0;
		}
	}
	inline bool check(int x)
	{
		for(int t:a[x].q)
		{
			if(!vis[t]) return 1;
		}
		return 0;
	}
	inline void main()
	{
		ios::sync_with_stdio(0);
		cin.tie(0),cout.tie(0);
		int skx;cin>>skx;
		while(skx--)
		{
			cin>>n;
			init();
			for(int i=1;i<=n;++i)
			{
				int k;cin>>k;
				while(k--)
				{
					int x;cin>>x;
					a[i].q.emplace_back(x);
				}
				a[i].id=i;
			}
			sort(a+1,a+n+1);
			bool flag=0;
			for(int i=1;i<=n&&!flag;++i)
			{
				for(int t:a[i].q)
				{
					vis[t]=1;
				}
				for(int t:a[i].q)
				{
					if(del[pre[t]]||pre[t]==0);
					else
					{
						if(check(pre[t]))
						{
							cout<<"YES\n";
							cout<<a[pre[t]].id<<' '<<a[i].id<<'\n';
							flag=1;
							break;
						}
						else del[pre[t]]=1;
					}
					pre[t]=i;
				}
				for(int t:a[i].q)
				{
					vis[t]=0;
				}
			}
			if(!flag)
			{
				cout<<"NO\n";
			}
		}
	}
}
signed main()
{
	red::main();
	return 0;
}
/*
3
5
4 1 2 3 5
3 1 2 3
2 1 2
1 1
1 4
4
3 1 2 3
3 2 3 4
0
4 1 2 3 4
2
3 1 3 5
3 2 3 4

*/

如何正确地排序

题意:

有一个\(m*n\)的数组\(a_{i,j}\)

定义\(f(i,j)=\min_{k=1}^n(a_{k,i}+a_{k,j})+max_{k=1}^n(a_{k,i}+a_{k,j})\)

\(\sum_{i=1}^n\sum_{j=1}^nf(i,j)\)

\(n\leq *10^5,2\leq m\leq 4\)

题解:

\(m=2\)时:

\(ans=2*n*a_{i,j}\)

\(m=3\)时:

考虑哪些位置有贡献:

\[B_i+B_j\leq A_i+A_j\\ B_i+B_j\leq C_i+C_j\\ \]

这是一个二维偏序,可以直接草

当然还有作为最大值的位置。

\(m=4\)时:

哈哈,当然可以三维偏序直接日,不过不优雅(不会写)

考虑正难则反,先让\(ans=2*n*a_{i,j}\)

然后去掉不合法的位置

\[A_i+A_j\leq B_i+B_j\\ B_i+B_j\leq C_i+C_j \]

变形一下

\[A_i-B_i\leq B_j-A_j\\ B_i-C_i\leq C_j-B_j \]

这样也就是二维偏序了,只不过要枚举哪些行要作为数组\(A,B,C\)

注意,这里如果\(m=3\)的时候用这个,每个\(B_i\)要减去\(2\)倍的贡献。

如果\(m=4\),则不用,因为本来是要减去两倍的,但是每个数字,如果它不是最值,它会被夹在中间两次,刚好减去两倍。!!

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define mid ((l+r)>>1)
#define lowbit(i) ((i)&(-i))
	const int N=5e5+10,inf=2e9;
	int n,m,ans;
	int a[4][N];
	struct BIT
	{
		int tr[N];
		inline void clear()
		{
			memset(tr,0,sizeof(tr));
		}
		inline void update(int x,int k)
		{
			for(int i=x;i<=4e5;i+=lowbit(i)) tr[i]+=k;
		}
		inline int query(int x)
		{
			int sum=0;
			for(int i=x;i>=1;i-=lowbit(i)) sum+=tr[i];
			return sum;
		}
	}T;
	struct node
	{
		int x,y,k;
		inline bool operator < (const node &t) const
		{
			if(x!=t.x) return x<t.x;
			return k<t.k;
		}
	}q[N];
	inline void work(int t1,int t2,int t3,int val)
	{	
		//cout<<t1<<' '<<t2<<' '<<t3<<"!!"<<endl;
		T.clear();
		//a[t1][i]-a[t2][i]<=a[t2][j]-a[t1][j]
		//a[t2][i]-a[t3][i]<=a[t3][j]-a[t2][j]
		int num=0;
		for(int i=1;i<=n;++i)
		{
			++num;
			q[num].x=a[t1][i]-a[t2][i]+(t1>t2);
			q[num].y=a[t2][i]-a[t3][i]+(t2>t3);
			q[num].k=0;
			++num;
			q[num].x=a[t2][i]-a[t1][i];
			q[num].y=a[t3][i]-a[t2][i];
			q[num].k=a[t2][i];
		}
		int pp=2e5;
		sort(q+1,q+num+1);
		for(int i=1;i<=num;++i)
		{
			//cout<<i<<' '<<num<<"!!"<<endl;
			if(q[i].k!=0)
			{
				//cout<<q[i].y<<"???"<<endl;
				ans-=val*T.query(q[i].y+pp)*q[i].k;
			}
			else
			{
				//cout<<q[i].y<<"!!"<<endl;
				T.update(q[i].y+pp,1);	
			}
		}
		//cout<<"--------------------"<<endl;
	}
	inline void main()
	{
		ios::sync_with_stdio(0);
		cin.tie(0),cout.tie(0);
		cin>>m>>n;
		for(int i=0;i<m;++i)
		{
			for(int j=1;j<=n;++j)
			{
				cin>>a[i][j];
				ans+=2*n*a[i][j];
			}
		}
		for(int i=0;i<m;++i)
			for(int j=0;j<m;++j)
				for(int k=0;k<m;++k)
					if(i!=j&&i!=k&&j!=k) work(i,j,k,5-m);
		cout<<ans<<'\n';
	}
}
signed main()
{
	red::main();
	return 0;
}
/*
2 3
1 4 5
6 5 4

4 5
1 7 2 2 7
9 10 4 10 3
7 7 8 10 2
1 8 7 9 5

*/

posted @ 2022-05-31 17:21  lovelyred  阅读(44)  评论(0编辑  收藏  举报