2021-2022年度第三届全国大学生算法设计与编程挑战赛(冬季赛)-正式赛 部分题解

2021-2022年度第三届全国大学生算法设计与编程挑战赛(冬季赛)-正式赛 部分题解,第1张

比赛链接

http://oj.saikr.com/contest/19

B.Error 思路

这道题其实我们贪心加上二分就能做了,首先我们贪心得把 b [ 1 ] b[1] b[1] 变得很小,然后构造 b [ i ] b[i] b[i] 的时候也是贪心的去构造,尽可能比 b [ i − 1 ] b[i-1] b[i1]大1,如果不行的话就让 b [ i ] = b [ i ] − e p s b[i] = b[i] - eps b[i]=b[i]eps ,然后我们对这个 e p s eps eps 的值进行二分查找就好了,详情请看代码

代码
#include
using namespace std;
//----------------自定义部分----------------
#define ll long long
#define mod 1000000007
#define endl "\n"
#define PII pair<int,int>
#define INF 0x3f3f3f3f
#define int long long
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

ll ksm(ll a,ll b) {
	ll ans = 1;
	for(;b;b>>=1LL) {
		if(b & 1) ans = ans * a % mod;
		a = a * a % mod;
	}
	return ans;
}

ll lowbit(ll x){return -x & x;}

const int N = 2e6+10;
//----------------自定义部分----------------
int t,n,m,q,a[N],b[N];

bool check(int eps){
	for(int i = 1;i <= n; ++i) b[i] = a[i];
	b[1] -= eps;
	b[1] = max(1LL,b[1]);
	for(int i = 2;i <= n; ++i) {
		if(b[i] + eps <= b[i - 1]) return false;
		if(b[i] - eps > b[i - 1])
				b[i] -= eps;
		else {
			if(b[i] + eps > b[i-1])
				b[i] = b[i-1] + 1;
			else return false;
		}
		b[i] = max(0LL,b[i]);
	}
//	for(int i = 1;i <= n; ++i) cout<
	return true;
}

void slove(){
	cin>>n;
	for(int i = 1;i <= n; ++i) cin>>a[i];
	int l = 0,r = 1e9+10;
	while(l < r) {
		int mid = l + r >> 1LL;
		if(check(mid)) r = mid;
		else l = mid + 1;
	}
	cout<<r<<endl;
}

signed main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	t = 1;
	while(t--){
		slove();
	}
	
	return 0;
}
E.吃利息 思路

按照他说的一步一步模拟就好了,注意的是:

  • 先计算利息,再加上多的5金币
  • 利息最多5块
代码
#include
using namespace std;
//----------------自定义部分----------------
#define ll long long
#define mod 1000000007
#define endl "\n"
#define PII pair<int,int>
#define INF 0x3f3f3f3f

int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

ll ksm(ll a,ll b) {
	ll ans = 1;
	for(;b;b>>=1LL) {
		if(b & 1) ans = ans * a % mod;
		a = a * a % mod;
	}
	return ans;
}

ll lowbit(ll x){return -x & x;}

const int N = 2e6+10;
//----------------自定义部分----------------
int t,k,n,m,q,a[N];
void slove(){
	cin>>k>>n;
	ll ans = k,cnt = 0;
	for(int i = 1;i <= n; ++i) {
		ans += min(ans/10,5LL);
		ans += 5;
	}
	cout<<ans<<endl;
}

int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	t = 1;
	while(t--){
		slove();
	}
	
	return 0;
}
F.Star 思路

计算凸包连边的概率就好,然后正常求凸包面积
ps:代码是借用的别人的模板

代码
#include
#define int long long
using namespace std;

//----------------自定义部分----------------
#define ll long long
#define mod 1000000007
#define endl "\n"
#define PII pair<int,int>
#define INF 0x3f3f3f3f
#define int long long

int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
ll t;
ll ksm(ll a,ll b) {
	ll ans = 1;
	for(;b;b>>=1LL) {
		if(b & 1) ans = ans * a % mod;
		a = a * a % mod;
	}
	return ans;
}

ll lowbit(ll x){return -x & x;}

//----------------自定义部分----------------
int n;
const int maxn=55;

struct point{
	double x,y;
	point (double a=0,double b=0):x(a),y(b){};
	point operator + (const point &a)const{
		return point (x+a.x,y+a.y);
	}
	point operator - (const point &a)const{
		return point (x-a.x,y-a.y);
	}
	double operator * (const point &a)const{
		return x*a.y-y*a.x;
	}
	friend double dist(const point &a){
		return a.x*a.x+a.y*a.y;
	}
	bool operator < (const point &a)const{
		if (x!=a.x) return x<a.x;
		else return y<a.y;
	}
};

class Constellation{
private:
	double pr[maxn];
	point nod[maxn];
	int n;
public:
	double expectation(vector <double> x, vector <double> y, vector <double> prob){
		double ans=0;
		n=x.size();
		for (int i=0;i<n;++i){
			nod[i]=point(x[i],y[i]);
			pr[i]=prob[i];
		}
		
		//枚举所有有向边。


for (int i=0;i<n;++i) for (int j=0;j<n;++j) if (i!=j){ double tmp=1; point use=nod[j]-nod[i]; //计算这条边在凸包上的概率 for (int k=0;k<n;++k) if (i!=k&&j!=k){ if (use*(nod[k]-nod[i])>0) tmp*=(1-pr[k]); else if (use*(nod[k]-nod[i])>-1e-12&&dist(nod[i]-nod[k])<dist(nod[j]-nod[i])) tmp*=(1-pr[k]); } //累计答案 ans+=tmp*(nod[i]*nod[j])*pr[i]*pr[j]; } //注意要除以2 ... ans/=2; return fabs(ans); } }CC; void slove(){ vector <double> x,y,prob; double a,b,c; cin>>n; for(int i = 0;i < n; ++i){ cin>>a>>b>>c; x.push_back(a); y.push_back(b); prob.push_back(c); } cout << fixed << setprecision(6); cout<<CC.expectation(x,y,prob)<<endl; } signed main() { ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); t = 1LL; while(t--) { slove(); } return 0; }

G.MP4 思路

实际上就是求解字典序第 k k k 小的数字,可以在leetcode找到原题:440. 字典序的第K小数字 ,实际上这题做法很多样,可以直接搜索,也可以看规律模拟(不知道为啥我的模拟写炸了,对拍到1e7的数据都没问题)

代码

模拟代码:

#include
using namespace std;
//----------------×Ô¶¨Ò岿·Ö----------------
#define ll long long
#define mod 1000000007
#define endl "\n"
#define PII pair<int,int>
#define INF 0x3f3f3f3f
#define int long long

int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

ll ksm(ll a,ll b) {
	ll ans = 1;
	for(;b;b>>=1LL) {
		if(b & 1) ans = ans * a % mod;
		a = a * a % mod;
	}
	return ans;
}

ll lowbit(ll x){return -x & x;}

const int N = 2e6+10;
//----------------×Ô¶¨Ò岿·Ö----------------
int t,n,m,q,a[N];

map<int,bool> vis;

void slove(){
	cin>>n;
	int cnt = 0;
	int p;
	for(int i = 1LL;i <= 9LL; ++i) {
		p = n;
		int j = i,c = 1LL;
		for(;j <= p; j *= 10LL,c *= 10LL){
			if(vis[j]) continue;
			vis[j] = true;
			cnt++;
			cout<<j<<".mp4"<<endl;
			if(cnt == 50) return;
		}
		if(j > p)
			j/=10LL,c/=10LL;
		for(int l = j;l > 1LL; l/=10LL,c/=10LL){
			int k = 1LL;
			j = l;
			while(j < n && k < c) {
				j++;
				k++;
				if(j % 10LL == 0LL && vis[j/10] == false) 
					cout<<(j/10LL)<<".mp4"<<endl,cnt++;
				if(cnt == 50LL) return;
				if(vis[j] == false){
					cout<<j<<".mp4"<<endl;
					cnt++;
					if(cnt == 50LL) return;
				}
			}
		}
	}
	
	
	
}

signed main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	t = 1;
	while(t--){
		slove();
	}
	
	return 0;
}

搜索代码:

#include
using namespace std;
//----------------自定义部分----------------
#define ll long long
#define mod 1000000007
#define endl "\n"
#define PII pair<int,int>
#define INF 0x3f3f3f3f
#define int long long

int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

ll ksm(ll a,ll b) {
	ll ans = 1;
	for(;b;b>>=1LL) {
		if(b & 1) ans = ans * a % mod;
		a = a * a % mod;
	}
	return ans;
}

ll lowbit(ll x){return -x & x;}

const int N = 2e6+10;
//----------------自定义部分----------------
int t,n,m,q;

class Solution {
public:
	int getSteps(int curr, long n) {
		int steps = 0;
		long first = curr;
		long last = curr;
		while (first <= n) {
			steps += min(last, n) - first + 1;
			first = first * 10;
			last = last * 10 + 9;
		}
		return steps;
	}
	
	int findKthNumber(int n, int k) {
		int curr = 1;
		k--;
		while (k > 0) {
			int steps = getSteps(curr, n);
			if (steps <= k) {
				k -= steps;
				curr++;
			} else {
				curr = curr*10;
				k--;
			}
		}
		return curr;
	}
}pp;


signed main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i = 1;i <= 50; ++i) {
		cout<<pp.findKthNumber(n,i)<<".mp4"<<endl;
	}
	
	return 0;
}
I.展览 思路

由于是代码填空题,并且注释写的非常清楚你要怎么做,所以大家看代码就好啦

代码
#include
#define ll long long
using namespace std;
const ll N=1e6+5;
ll n,b[N],a[N],ans;
//b[i]表示二进制下的第i位
void update(ll x){
	for(ll i=60;i>=0;i--){
		if(x>>i){//如果x在二进制表示下含有第i位
			if(b[i])x^=b[i];//如果b[i]存在则让x^b[i],
			//因为之前b[i]也是由已经保存过的a[]数组贡献的
			//所以,这样异或x可以看作x于之前的a[]数组进行异或
			//然后一直异或到为0或者当前b[i]还没有被赋值
			else {  b[i]=x;break;}//否则b[i]赋值为x,
			//表示当前二进制下的第i位可以被异或出来,且x的最高位就是i
		}
	}
}

int main(){
	cin>>n;
	for(ll i=1;i<=n;i++){cin>>a[i];update(a[i]);}//读入数据对于每一个数字都下放来维护b[i]
	for(ll i=60;i>=0;i--)if((ans^(1ll<<i))>ans)ans^=b[i];
	//贪心的过程,ans看作一个二进制数,从高位开始,如果b[i]存在,
	//肯定优先跟b[i]异或,倒着让小值不会影响到大值
	cout<<ans<<endl;
	return 0;
}

K.礼物 思路

排序,然后输出最大即可

代码
#include
using namespace std;
//----------------自定义部分----------------
#define ll long long
#define mod 1000000007
#define endl "\n"
#define PII pair<int,int>
#define INF 0x3f3f3f3f

int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

ll ksm(ll a,ll b) {
	ll ans = 1;
	for(;b;b>>=1LL) {
		if(b & 1) ans = ans * a % mod;
		a = a * a % mod;
	}
	return ans;
}

ll lowbit(ll x){return -x & x;}

const int N = 2e6+10;
//----------------自定义部分----------------
int t,n,m,q,a[N];
void slove(){
	cin>>n;
	for(int i = 0;i < n; ++i) cin>>a[i];
	sort(a,a+n);
	cout<<a[n-1]<<endl;
}

int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	t = 1;
	while(t--){
		slove();
	}
	
	return 0;
}
L.看错题 思路

直接每次都从上往下查询的话复杂度就是 n 2 l o g 2 n n^2log_2 n n2log2n显然不行的,我们其实只需要按照他的要求从每个叶子节点到根节点求一次初始状态的和ans,然后再后面的 update 的过程中动态更新ans 就好了,注意的是如果我们在一个完全包含我们修改区间的时候,直接计算一下权值更新和就好了,实际上到后面你会发现完全不用维护每个节点的区间和,只需要计算区间之间的关系即可,详情请看update 函数中的 *** 作

代码
#include
using namespace std;

#define ll long long

const int N = 2e5+10;

ll a[N];
ll n,m;

ll b[N<<2];

struct Tnode{
	ll l,r,sum,lazy;
};
ll ans = 0;
struct SegmentTree{
	
	Tnode tree[N<<2];
	
	inline void init_lazy(ll root){//清空lazy标记
		tree[root].lazy = 0;
	}
	
	inline void push_up(ll root){//向上更新
		push_down(root<<1);
		push_down(root<<1 | 1);
		tree[root].sum = tree[root<<1].sum + tree[(root<<1) | 1].sum;
	}
	inline void push_down(ll root){//向下更新
		if(tree[root].lazy != 0){
			tree[root].sum += tree[root].lazy * (tree[root].r - tree[root].l + 1);
			if(tree[root].l != tree[root].r){//第二步,下推lazy标记
				tree[root<<1].lazy += tree[root].lazy;
				tree[(root<<1) | 1].lazy += tree[root].lazy;
			}
			init_lazy(root);//第三步,清空root的lazy标记,因为已经下推过了
		}
	}
	
	void build(ll l,ll r,ll root){//建树
		tree[root].l = l;
		tree[root].r = r;
		init_lazy(root);//初始化lazy节点
		if(l == r) tree[root].sum = a[l];
		else{
			ll mid = ((r+l) >> 1);
			build(l,mid,root<<1);
			build(mid+1,r,(root<<1)+1);
			push_up(root);
		}
	}
	void update(ll l,ll r,ll root,ll add){//区间更新,在 [l,r] 区间都加上add
		push_down(root);//向下更新,因为有可能还存在lazy节点没更新
		if(tree[root].l == tree[root].r){
			tree[root].sum += add;
			ans += add;
			return;
		}
		else if(tree[root].l >= l && tree[root].r <= r){
			tree[root].sum += add;//这里的懒标记直接标记为add就好了
			ll len = b[root] * (2LL * b[root] - 1LL);
			ans += add * len;
		}
		else{
			if(tree[root].l <= l && tree[root].r >= r){
				ans += add * (r - l + 1) * b[root];
			} 
			else if(tree[root].l <= r){
				ans += add * (max(min(tree[root].r,r) - max(tree[root].l,l) + 1LL,0LL)) * b[root];
			}
			ll mid = ((tree[root].r+tree[root].l) >> 1);
			if(l <= mid) {
				update(l,r,root<<1,add);
			}
			if(r > mid)  {
				update(l,r,(root<<1)+1,add);
			}
			push_up(root);//向上更新
		}
	}
	ll query(ll l,ll r,ll root) {//区间查询
		push_down(root);//下推标记,可能还有lazy节点没更新
		if(tree[root].l >= l && tree[root].r <= r) return tree[root].sum;
		else{
			ll mid = tree[root].l + ((tree[root].r-tree[root].l) >> 1);
			ll ans = 0LL;
			if(l <= mid) ans += query(l,r,root<<1);//如果我们查询区间的左边界比当前节点的中间点小,那么说明查询区间要往左走
			if(r > mid)  ans += query(l,r,(root<<1) + 1);//如果我们查询的右边界要比当前节点的中间节点大,那么说明查询区间要往右走
			return ans;
		}
	}
	ll find(ll root) {
		push_down(root);
		if(tree[root].l == tree[root].r) return tree[root].sum;
		ll mid = tree[root].l + ((tree[root].r-tree[root].l) >> 1);
		ll ans = 0LL;
		ans += find(root<<1);
		ans += find((root<<1) + 1);
		ans += tree[root].sum * b[root];//(r - l + 1);//
		return ans;
	}
}Tree;

void dfs(ll root) {
	if(root >= n) {
		b[root] = 1;
		return;
	}
	ll l = root,r = root;
	while(l * 2LL < n * 2) l *= 2LL;
	while((r * 2LL + 1) < n * 2) r = r * 2LL + 1;
	dfs(root<<1);
	dfs((root<<1) | 1);
	b[root] = r - l + 1;
	
}



int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i = 1;i <= n; ++i) cin>>a[i];
	Tree.build(1,n,1);
	dfs(1LL);
	ans = Tree.find(1LL);
	
	ll op,k,l,r;
	for(int t = 1;t <= m; ++t){
		cin>>l>>r>>k;
		Tree.update(l,r,1LL,k);
		cout<<ans<<endl;
//		cout<
	}
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/563162.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-02
下一篇 2022-04-02

发表评论

登录后才能评论

评论列表(0条)

保存