2021.06.12模拟总结

概述

端午假期的第一天集训,进行这次组队模拟
学长:难度 (color{rgb(243, 156, 17)}{橙}spacecolor{rgb(52, 152, 219)}{蓝space蓝})

T1 立方数(cubicp)

题意

给定质数(p),求是否满足(exists a,b),使得(p=a^3-b^3)

解析

(p=a^3-b^3)转化一下,得到:

(p=a^3-b^3space =(a-b) imes(a^2+ab+b^2))
其中,因为(p)是质数,那么分解出来的两个因数(a-b)(a^2+ab+b^2)一个为(1),一个为(p)
显然有(|a-b|=1)(a^2+ab+b^2=p)
所以原式转化为:(b=a+1Rightarrow a^2+acdot(a+1)+(a+1)^2=p)
得到:(3a^2+3a+1=p)

由于这样得到的(a)较小,直接枚举(a)即可。
复杂度(O(Tsqrt p))

或者,因为(a)的成立性有单调性,可以用二分来优化枚举过程。
复杂度(O(Tlog{sqrt p}))

代码

#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std;
const int INF = 0x3f3f3f3f , N = 1e2;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll ret = 0 ; char ch = ' ' , c = getchar();
	while(!(c >= '0' && c <= '9'))ch = c, c = getchar();
	while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
	return ch == '-' ? -ret : ret;
}
ll check(ll x){return 3*x*x + 3*x + 1;}
inline bool work(){
	ll p = read();
	ll l = 1 , r = 1e9;
	while(l < r){
		int mid = (l + r + 1) >> 1;
		if(check(mid) <= p) l = mid;
		else r = mid - 1;
	}
	return check(l) == p;
}
signed main(){
//	fo("cubicp");
	int T = read();
	while(T--)
		printf("%s
",work() ? "YES" : "NO");
	return 0;
}

T2 动态规划

题目名称言简意赅

题意

给定长度为(n)序列,分成(k)段,求每段相同数字对数的最小和。

解析

很考验思维的一道题,需要用决策单调性优化。
首先,设(dp[i][j])表示前(i)个数字分为(j)段的最小答案。
不难得到状态转移方程:(dp[l][r]=dp[l][k]+calc(k+1,r))
发现方程符合四边形不等式。
使用类似莫队的方法优化这个过程,
具体详见代码。(复杂度为(O(nlog n k)))

一些问题

  • 关于adddel的先加后加问题:
    • Add一个数字(x),对答案的贡献(=)序列内已有的(x)的个数(加入的数和其余数分别组对),所以Add是ans += buc[a[x]]++;
    • Del一个数字(x),对答案的贡献(=)序列内已有的(x)的个数-1(删除的数和其余数分别组队),所以Del是ans -= --buc[a[x]];
  • 关于move的先加后加问题:
    • 区间扩展一个,与上Add
    • 区间减小一个,与上Del
  • 另外:区间移动时倒序比正序略快(不知为何的玄学优化)

代码

#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std;
const int INF = 0x3f3f3f3f , N = 1e5+5 , M = 22;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll ret = 0 ; char ch = ' ' , c = getchar();
	while(!(c >= '0' && c <= '9'))ch = c, c = getchar();
	while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
	return ch == '-' ? -ret : ret;
}
int n,k;
int a[N],buc[N];
int pl,pr,cur;
ll ans,dp[N][M];
inline void add(int x){ans += buc[a[x]]++;}
inline void del(int x){ans -= --buc[a[x]];}
inline ll calc(int l,int r){
	while(pl > l)add(--pl);
	while(pr < r)add(++pr);
	while(pl < l)del(pl++);
	while(pr > r)del(pr--);
	return ans;
}
void dfs(int l,int r,int pl,int pr){
	if(l > r)return ;
	int mid = (l + r) >> 1,p;
	dp[mid][cur] = 1LL*INF*INF;
	for(int i =  min(mid-1,pr) ; i >= pl ; i --){
		ll t = dp[i][cur-1] + calc(i+1,mid);
		if(t < dp[mid][cur])
			dp[mid][cur] = t,p = i;
	}
	dfs(l,mid-1,pl,p);
	dfs(mid+1,r,p,pr);
}

signed main(){
	n = read() , k = read();
	pl = 1 , pr = 0;
	for(int i = 1 ; i <= n ; i ++)
		a[i] = read();
	for(int i = 1 ; i <= n ; i ++)
		dp[i][1] = calc(1,i);
	for(int i = 2 ; i <= k ; i ++)
		cur = i , dfs(1,n,0,n);
	printf("%lld",dp[n][k]);
	return 0;
}

3.游戏

题意

(n)个数字和(T)次给定询问,每次询问给定([l,r])的最小值(x),求第几次操作是矛盾的。

解析

对于(T)次询问,可以先进行按(x)从大到小排序。
对于每一些(x)相同的区间,它们会有交集和并集。维护这个交集和并集。

  • 对于交集:是能确定(x)在的最小的区间,若此段已锁定是更大的值而无处安放,则矛盾
  • 对于并集:是(x)所在最大的区间,也就是上面提到的锁定
    可以维护一个线段树,每个叶节点均为1.若锁定则改为0.
    查询时,找到最区间,判断区间和是否为0即可。
    修改时,找到最区间,修改为0即可。

复杂度:(O(nlog n log T))

代码

#include <bits/stdc++.h>
#define fo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std;
const int INF = 0x3f3f3f3f , N = 1e6;
typedef long long ll;
typedef unsigned long long ull;
inline ll read(){
	ll ret = 0 ; char ch = ' ' , c = getchar();
	while(!(c >= '0' && c <= '9'))ch = c, c = getchar();
	while(c >= '0' && c <= '9')ret = (ret << 1) + (ret << 3) + c - '0' , c = getchar();
	return ch == '-' ? -ret : ret;
}
int n,T;
struct node{int id,l,r,x;}a[N]; inline bool operator < (node a,node b){return a.x > b.x;}

int tree[N<<2];
void modify(int k,int l,int r,int x,int y){
	if(!tree[k])return ;
	if(x <= l && r <= y){tree[k] = 0;return;}
	int mid = (l + r) >> 1;
	if(x <= mid) modify(k<<1,l,mid,x,y);
	if(y >= mid + 1) modify(k<<1|1,mid+1,r,x,y);
	tree[k] = tree[k<<1] + tree[k<<1|1];
}
int query(int k,int l,int r,int x,int y){
	if(!tree[k])return 0;
	if(x <= l && r <= y)return tree[k];
	int mid = (l + r) >> 1 , ret = 0;
	if(x <= mid) ret += query(k<<1,l,mid,x,y);
	if(y >= mid + 1) ret += query(k<<1|1,mid+1,r,x,y);
	return ret;
}
void build(int k,int l,int r){
	if(l == r) {tree[k] = 1;return;}
	int mid = (l + r) >> 1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	tree[k] = tree[k<<1] + tree[k<<1|1];
}
inline bool check(int x){
	build(1,1,n);
	int now = INF , l = INF , r = -INF , cl = -INF , cr = INF;
	for(int i = 1 ; i <= T ; i ++)
		if(a[i].id <= x){
			node f = a[i];
			if(a[i].x == now){
				l = min(l,a[i].l);
				r = max(r,a[i].r);
				cl = max(cl,a[i].l);
				cr = min(cr,a[i].r);
			}
			else{
				if(query(1,1,n,cl,cr))
					modify(1,1,n,l,r);
				else return 0;
				now = a[i].x;
				cl = l = a[i].l , cr = r = a[i].r;
			}
		}
	return query(1,1,n,cl,cr);
}
signed main(){
//	fo("number");
	n = read() , T = read();
	for(int i = 1 ; i <= T ; i ++)
		a[i].l = read() , a[i].r = read() , a[i].x = read() , a[i].id = i;
	sort(a+1,a+n+1);
	int l = 1 , r = T;
	while(l < r){
		int mid = (l + r + 1)>>1;
		int tmp;
		if(tmp = check(mid)) l = mid;
		else r = mid-1;
	}
	printf("%d",l+1);
	return 0;
}
原文地址:https://www.cnblogs.com/Shinomiya/p/14878558.html