NOI Online #2 赛后题解

T1【color】涂色游戏

【Description】 https://www.luogu.com.cn/problem/P6476
比较水的一道题。
用\(gcd(p_1,p_2)\)和贪心乱搞一通,用裴蜀定理证明,没了。
但是千万千万记得要特判\(k=1\)
时间复杂度:\(O(T\;log\;p)\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define LL long long
LL a, b, k, T;
LL gcd(LL a,LL b)
{
	if(b == 0) return a;
	return gcd(b , a % b);
}
int main()
{
	cin>>T;
	while(T--)
	{
		scanf("%lld%lld%lld",&a,&b,&k);
		if(k == 1)
		{
			puts("No");
			continue;
		}
		if(a > b)swap(a,b);
		LL g = gcd(a,b);
		LL t = ((b - g) % a == 0) ? 0 : 1;
		if((b - g) / a + t < k)puts("Yes");
		else puts("No");
	}
	return 0;
} 

T2【sequence】子序列问题

【Description】 https://www.luogu.com.cn/problem/P6477

暴力

直接枚举\(l,r\),然后在\(r\)不断加\(1\)的同时,更新此时区间中数字种类个数
时间复杂度:\(O(n^2)\)
期望得分:\(50pts\)


正解

直接求平方似乎不太好操作
\(\because f(l,r)^2=f(l,r)(f(l,r)-1)+f(l,r)\)
不妨设\(F(n)=\sum_{i=1}^n f(i,n)\;,\;g(n)=\sum_{i=1}^n f(i,n)(f(i,n)-1)\)
然后我们只需分别求\(\sum_{i=1}^n F(n),g(n)\)即可
关键是怎么求?


现在给出一个思路:求\(F(n)-F(n-1)\)与\(g(n)-g(n-1)\)
而\(F(n)\)中的每个区间只比\(F(n-1)\)中的区间多了\(a_n\)
所以我们预处理出\(last_n=j\),\(j<n\)且\(a_j=a_n\)(\(j\)是满足这个条件中最靠后的)
因此只有\([last_n+1,n]\)为\(l\)端点的区间才会多出\(a_r\)这个新的数字,产生\(1\)的贡献,总共多产生了\(n-last_n\)的贡献
\(\therefore F(n)-F(n-1)=n-last_n\)
而这个玩意相当于把\([last_n+1,n]\)这段区间执行\(+1\)的操作


而求\(g(n)-g(n-1)\)与\(F\)同理。
\([last_n+1,n]\)为\(l\)端点的区间,多产生\(1\)的贡献。
因此:这些区间的\(f(l,r)(f(l,r)-1)=>f(l,r)(f(l,r)+1)\)
\(\because f(l,r)(f(l,r)+1)-f(l,r)(f(l,r)-1)=2\times f(l,r)\)
所以总共会增长:\(2\times \sum_{i=last_n+1}^n f(i,r)\)
而:\(\sum_{i=last_n+1}^n f(i,r)\)实际是一个区间求和的操作


区间执行\(+1\),区间求和你想到了什么?
没错,就是线段树。
因此在枚举\(r\)(右端点)的过程中用线段树维护区间修改与和。
注意在修改与求和的顺序。
时间复杂度:\(O(n\;log\;n)\)
期望得分:\(100pts\)


\(code:\)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1000010, mod = 1000000007;
#define LL long long 
#define F(i,a,b) for(int i=a;i<=b;i++)
#define ls(x) x<<1
#define rs(x) x<<1|1
int n, a[N], b[N], ap[N], last[N];
LL tree[N << 2], tag[N << 2], f[N], g[N], res1, res2;

inline void push_up(int root)
{
	tree[root] = (tree[ls(root)] + tree[rs(root)]) % mod; 
}
void build (int root, int l, int r)
{
	if(l==r)
	{
		return;
	}
	int mid = (l + r) >> 1;
	build(ls(root), l, mid);
	build(rs(root), mid+1, r);
	push_up(root);
}
inline void change(int root, int l, int r, LL k)
{
	tag[root] = (tag[root] + k) % mod;
	tree[root] = (tree[root] + (r-l+1) * k % mod) % mod;
}
void push_down(int root, int l, int r)
{
	if(tag[root])
	{
		int mid = (l + r) >> 1;
		change(ls(root), l, mid, tag[root]);
		change(rs(root), mid+1, r, tag[root]);
	}
	tag[root] = 0;
}
void Update(int root, int L, int R, int l, int r, LL k)
{
	if(l<=L&&R<=r)
	{
		change(root,L,R,k);
		return;
	}
	push_down(root,L,R);
	int mid = (L + R) >> 1;
	if(l <= mid) Update(ls(root), L, mid, l, r, k);
	if(r > mid) Update(rs(root), mid+1, R, l, r, k);
	push_up(root);
}
LL Query(int root, int L, int R, int l, int r)
{
	LL res = 0;
	if(l<=L&&R<=r)
	{
		return tree[root];
	}
	push_down(root,L,R);
	int mid = (L + R) >> 1;
	if(l <= mid) res = (res + Query(ls(root), L, mid, l, r)) % mod;
	if(r > mid) res = (res + Query(rs(root), mid+1, R, l, r)) % mod;
	return res;
}
int main()
{
	scanf("%d",&n);
	F(i,1,n) 
	{
		scanf("%d",&a[i]);
		b[i]=a[i];
	}
	sort(b+1,b+n+1);
	int m = unique(b+1,b+n+1) - b - 1;
	F(i,1,n)
	{
		a[i] = lower_bound(b+1,b+m+1,a[i]) - b;
	}
	F(i,1,n)
	{
		last[i]=ap[a[i]];
		ap[a[i]]=i;
	}
	build(1,1,n);
	F(r,1,n)
	{
		f[r] = (f[r-1] + r - last[r]) % mod;
		res1 = (res1 + f[r]) % mod;
		g[r] = (g[r-1] + 2 * Query(1,1,n,last[r]+1,r)) % mod;
		res2 = (res2 + g[r]) % mod;
		Update(1,1,n,last[r]+1,r,1);
	}
	printf("%lld",(res1 + res2) % mod);
	return 0;
}
上一篇:noi online round2(入门组)


下一篇:在线教育「数智增长营」Online 开营啦!