CF1771

CF1771

Hossam and Combinatorics

建议降红

相当于是最大值个数和最小值个数乘积再乘上 2

需要注意特判最大值和最小值相等的情况 这时候的答案是 n(n1)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define getchar() cin.get()
#define int long long
const int inf = 0x3f3f3f3f;
const int N = 3e5 + 5;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , a[N] , maxx , minn , maxxcnt , minncnt;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	int T = read();
	while ( T -- )
	{
		n = read();
		maxx = maxxcnt = minncnt = 0 , minn = inf;
		for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , maxx = max ( maxx , a[i] ) , minn = min ( minn , a[i] );
		for ( int i = 1 ; i <= n ; i ++ )
		{
			if ( maxx == a[i] ) ++ maxxcnt;
			if ( minn == a[i] ) ++ minncnt;
		}
		cout << ( maxx == minn ? ( n * ( n - 1 ) ) : maxxcnt * minncnt * 2 ) << endl;
	}
	return 0;
}

Hossam and Friends

我们设 a[i] 表示 a=i 时最大的 b

对于一个点对 (x,y) 我们可以将 a[x] 更新为 min(a[x],y1)

然后我们从后往前更新 a[i]=min(a[i],a[i+1]) 这是因为上一个的最远点一定是这一个的最远点 因为上一个的终点 b 再大就不满足 yi>b 了 那么这一个的终点也不应该比上一个的终点大(因为这一个的起点 a 还要比上一个的起点 a 小 更有可能覆盖上一段点对)

注意 x 有可能大于 y 需要交换一下

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define getchar() cin.get()
#define int long long
const int N = 3e5 + 5;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , a[N] , m;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	int T = read();
	while ( T -- )
	{
		n = read() , m = read();
		fill ( a + 1 , a + n + 1 , n );
		for ( int i = 1 , x , y ; i <= m ; i ++ ) x = read() , y = read() , a[min(x,y)] = min ( a[min(x,y)] , max(x,y) - 1 );
		for ( int i = n - 1 ; i ; i -- ) a[i] = min ( a[i] , a[i+1] );
		int ans = 0;
		for ( int i = 1 ; i <= n ; i ++ ) ans += ( a[i] - i + 1 );
		cout << ans << endl;
	}
	return 0;
}

Hossam and Trainees

明显比上一场简单...

我们可以先筛出 n(n=1e9) 之间的所有质数 开一个 cnt 数组记录每一个质数作为因数的次数

我们对于一个数 将它质因数分解并记录在 cnt 数组中

对于 >n 的因数来说 因为最多这样的因数只有一个 那么我们可以用 map 来记录这个因数是否被访问过了

最后我们遍历 cnt 数组并统计是否有被统计超过两次的质因数 输出答案即可

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define getchar() cin.get()
const int N = 1e5 + 5;
const int maxn = sqrt(1e9);

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , a[N] , ans , cnt[N];
map<int,int> mp;

int isntprime[N] , prime[N] , tot;
void pre ()
{
	for ( int i = 2 ; i <= maxn ; i ++ )
	{
		if ( !isntprime[i] ) prime[++tot] = i;
		for ( int j = 1 ; j <= tot && i * prime[j] <= maxn ; j ++ )
		{
			isntprime[i*prime[j]] = 1;
			if ( ! ( i % prime[j] ) ) break;
		}
	}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	int T = read();
	pre();
	while ( T -- )
	{
		ans = 0 , mp.clear();
		memset ( cnt , 0 , sizeof cnt );
		n = read();
		for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
		for ( int i = 1 ; i <= n ; i ++ )
		{
			for ( int j = 1 ; j <= tot && a[i] >= prime[j] ; j ++ )
				if ( ! ( a[i] % prime[j] ) )
				{
					++ cnt[j];
					while ( ! ( a[i] % prime[j] ) ) a[i] /= prime[j];
				}
			if ( a[i] > 1 )
			{
				if ( mp[a[i]] ) { ans = 1; break; }
				mp[a[i]] = 1;
			}
		}
		for ( int i = 1 ; i <= tot ; i ++ ) ans |= ( cnt[i] >= 2 );
		cout << ( ans ? "YES" : "NO" ) << endl;
	}
	return 0;
}

Hossam and (sub-)palindromic tree

难度起来了()

先考虑序列上的情况 设 f(l,r) 表示 slr 的最长回文子序列长度 可得到转移方程:

f(l,r)=max{f(l+1,r),f(l,r1),f(l+1,r1)+2[sl=sr]}

将这个东西转移到树上 即设 f(x,y) 表示 x,y 路径构成的最长回文子序列长度 fa(x,y) 表示以 x 为根时 y 的父亲 可得转移方程:

f(x,y)=max{f(fa(y,x),y),f(x,fa(x,y)),f(fa(y,x),fa(x,y))+2[sx=sy]}

最终答案即为 maxx,yf(x,y) 记忆化搜索实现即可

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define getchar() cin.get()
const int N = 2e3 + 5;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , f[N][N] , fa[N][N] , dep[N][N] , ans;
string s;

vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }

void clear()
{
	for ( int i = 1 ; i <= n ; i ++ ) for ( int j = 1 ; j <= n ; j ++ ) fa[i][j] = dep[i][j] = 0 , f[i][j] = -1;
	for ( int i = 1 ; i <= n ; i ++ ) e[i].clear();
	ans = 0;
}

void dfs ( int rt , int u , int ff )
{
	fa[rt][u] = ff , dep[rt][u] = dep[rt][ff] + 1;
	for ( auto v : e[u] ) if ( v ^ ff ) dfs ( rt , v , u );
}

int dp ( int x , int y )
{
	if ( f[x][y] != -1 ) return f[x][y];
	if ( dep[x][y] == 1 ) return f[x][y] = 1;
	if ( dep[x][y] == 2 ) return f[x][y] = ( s[x] == s[y] ) + 1;
	return f[x][y] = max ( max ( dp ( fa[y][x] , y ) , dp ( x , fa[x][y] ) ) , dp ( fa[y][x] , fa[x][y] ) + ( ( s[x] == s[y] ) ? 2 : 0 ) );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	int T = read();
	while ( T -- )
	{
		n = read();
		clear();
		cin >> s; s = " " + s;
		for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
		for ( int i = 1 ; i <= n ; i ++ ) dfs ( i , i , 0 );
		for ( int i = 1 ; i <= n ; i ++ ) for ( int j = i ; j <= n ; j ++ ) ans = max ( ans , dp ( i , j ) );
		cout << ans << endl;
	}
	return 0;
}

Hossam and a Letter

类似种花的题

记录 up[i][j][0/1] 表示从 (i,j) 点向上走 经过 0/1m 能到达的最远点 down[i][j][0/1] 同理

那么我们 O(n3) 地枚举每一条横杠进行统计即可(具体来说 枚举横杠的左端点 然后再枚举右端点的纵坐标即可)

注意当 (i,j) 本身是 m 点的时候 要提前处理使得 up[i][j][0]=up[i][j][1] down 也同理 这是为了在查询的时候不受到枚举的横杠左端点是 m 点的干扰

细节很多 状态转移方程不要挂了

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+(r-l)/2)
// #define mid ((l+r)/2)
#define ls(p) t[p].son[0]
#define rs(p) t[p].son[1]
#define lson ls(p),l,mid
#define rson rs(p),mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define getchar() cin.get()
// #define int long long
const int N = 1e3 + 5;
const int maxn = 2e9; 

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , up[N][N][2] , dw[N][N][2] , ans;

char mp[N][N];

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = 1 ; j <= m ; j ++ )
			cin >> mp[i][j];
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = 1 ; j <= m ; j ++ ) 
		{
			if ( mp[i][j] == '#' ) up[i][j][0] = up[i][j][1] = 0;
			else if ( mp[i][j] == '.' ) up[i][j][0] = up[i-1][j][0] + 1 , up[i][j][1] = up[i-1][j][1] + 1;
			else up[i][j][0] = 0 , up[i][j][1] = up[i-1][j][0] + 1;
		}
	for ( int i = n ; i ; i -- )
		for ( int j = 1 ; j <= m ; j ++ ) 
		{
			if ( mp[i][j] == '#' ) dw[i][j][0] = dw[i][j][1] = 0;
			else if ( mp[i][j] == '.' ) dw[i][j][0] = dw[i+1][j][0] + 1 , dw[i][j][1] = dw[i+1][j][1] + 1;
			else dw[i][j][0] = 0 , dw[i][j][1] = dw[i+1][j][0] + 1;
		}
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = 1 ; j <= m ; j ++ ) 
			if ( mp[i][j] == 'm' ) up[i][j][0] = up[i][j][1] , dw[i][j][0] = dw[i][j][1];
	for ( int i = 2 ; i < n ; i ++ )
		for ( int j = 1 ; j <= m - 2 ; j ++ )//横杠左端点(i,j)
		{
			int cnt = ( mp[i][j] == 'm' ) + ( mp[i][j+1] == 'm' );
			if ( cnt > 1 || mp[i][j] == '#' || mp[i][j+1] == '#' ) continue;
			for ( int k = j + 2 ; k <= m ; k ++ )//横杠右端点(i,k)
			{
				if ( mp[i][k] == '#' ) break;
				cnt += ( mp[i][k] == 'm' );
				if ( cnt > 1 ) break;
				int res = 0;
				if ( up[i][j][0] > 1 && up[i][k][0] > 1 && dw[i][j][0] > 1 && dw[i][k][0] > 1 ) res = max ( res , k - j - 3 + min ( up[i][j][0] , up[i][k][0] ) * 2 + min ( dw[i][j][0] , dw[i][k][0] ) * 2 );
				if ( !cnt ) 
				{
					if ( up[i][j][1] > 1 && up[i][k][0] > 1 && dw[i][j][0] > 1 && dw[i][k][0] > 1 ) res = max ( res , k - j - 3 + min ( up[i][j][1] , up[i][k][0] ) * 2 + min ( dw[i][j][0] , dw[i][k][0] ) * 2 );
					if ( up[i][j][0] > 1 && up[i][k][1] > 1 && dw[i][j][0] > 1 && dw[i][k][0] > 1 ) res = max ( res , k - j - 3 + min ( up[i][j][0] , up[i][k][1] ) * 2 + min ( dw[i][j][0] , dw[i][k][0] ) * 2 );
					if ( up[i][j][0] > 1 && up[i][k][0] > 1 && dw[i][j][1] > 1 && dw[i][k][0] > 1 ) res = max ( res , k - j - 3 + min ( up[i][j][0] , up[i][k][0] ) * 2 + min ( dw[i][j][1] , dw[i][k][0] ) * 2 );
					if ( up[i][j][0] > 1 && up[i][k][0] > 1 && dw[i][j][0] > 1 && dw[i][k][1] > 1 ) res = max ( res , k - j - 3 + min ( up[i][j][0] , up[i][k][0] ) * 2 + min ( dw[i][j][0] , dw[i][k][1] ) * 2 );
				}
				ans = max ( ans , res );
			}
		}
	cout << ans << endl;
	return 0;
}

Hossam and Range Minimum Query

一个粗略的想法是我们用主席树维护异或前缀和 因为异或的优秀性质 我们可以得知 如果区间内该数的出现次数是偶数 那么整体异或值为 0

但这样做显然是错的 例如 (1100)2(0011)2=(1111)2 这三个数都只出现了一次 但是异或起来为 0

我们考虑为每一个值赋一个随机哈希值 显然这样出现错误的概率会小很多 用主席树维护即可

注意这里我们如果不开 long long 的话 对于 middefine 就需要采用 (l+(rl)/2) 的形式来防止溢出 int

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define mid (l+(r-l)/2)
// #define mid ((l+r)/2)
#define ls(p) t[p].son[0]
#define rs(p) t[p].son[1]
#define lson ls(p),l,mid
#define rson rs(p),mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define getchar() cin.get()
// #define int long long
const int N = 2e5 + 5;
const int maxn = 2e9; 

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , q , a[N] , root[N] , lstans;
string s;
map<int,int> mp;

struct Tree
{
	struct node { int son[2] , sum; } t[N<<5];
	int tot = 0;
	inl int new_node ( int x ) { t[++tot] = t[x]; return tot; }
	void upd ( int &p , int l , int r , int x , int val )
	{
		p = new_node(p) , t[p].sum ^= val;
		if ( l == r ) return;
		if ( x <= mid ) upd ( lson , x , val );
		else upd ( rson , x , val );
	}
	int query ( int u , int v , int l , int r )
	{
		if ( l == r ) return l;
		if ( t[ls(u)].sum ^ t[ls(v)].sum ) return query ( ls(u) , ls(v) , l , mid );
		else return query ( rs(u) , rs(v) , mid + 1 , r );
	}
}T;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	mt19937 rnd(random_device{}());
	n = read();
	for ( int i = 1 ; i <= n ; i ++ )
	{
		a[i] = read();
		if ( !mp.count(a[i]) ) mp[a[i]] = rnd();
		T.upd ( root[i] = root[i-1] , 0 , maxn , a[i] , mp[a[i]] );
	}
	q = read();
	for ( int i = 1 ; i <= q ; i ++ )
	{
		int l = read() ^ lstans , r = read() ^ lstans;
		lstans = T.query ( root[l-1] , root[r] , 0 , maxn );
		if ( lstans == maxn ) lstans = 0;
		cout << lstans << endl;
	}
	return 0;
}
posted @   Echo_Long  阅读(490)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示