2021杭电多校赛2021“MINIEYE杯”中国大学生算法设计超级联赛签到题4题

Posted 小哈里

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021杭电多校赛2021“MINIEYE杯”中国大学生算法设计超级联赛签到题4题相关的知识,希望对你有一定的参考价值。

Solved Pro.ID Title Ratio(Accepted / Submitted)
1001 Miserable Faith 33.33%(19/57)
1002 String Mod 26.50%(31/117)
1003 VC Is All You Need 41.56%(845/2033)(结论)
1004 Another String 57.30%(102/178)
1005 Random Walk 2 52.67%(79/150)
1006 Cute Tree 64.94%(876/1349)(模拟)
1007 Banzhuan 19.98%(664/3324)(贪心)
1008 Supermarket 61.40%(35/57)
1009 Array 15.03%(112/745)思维
1010 Guess Or Not 2 0.00%(0/7)
1011 Jsljgame 1.22%(1/82)
1012 Yet Another Matrix Problem 15.45%(17/110)
1013 Penguin Love Tour 14.83%(39/263)

1003 VC Is All You Need

题意:

  • n维空间中,求m的最大值,使得你可以找到m个点(自己给定坐标),满足:无论对这m个点如何二染色,也就是对于2^m种染色方案中的每一种,都总存在一个n-1维超平面,严格分开这两种颜色的点。

思路:

  • 结论:Max{m} = n+1
  • 证明:显然有单调性,可以分两步证明结论,
    1,证明m=n+1可行。
    2,证明m>=n+2无解。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T;  cin>>T;
    while(T--){
		LL n, k;  cin>>n>>k;
        if(k>=n-1)cout<<"Yes\\n";
		else cout<<"No\\n";
    }
    return 0;
}

1006 Cute Tree

题意:

  • 给出一个假算法,用一个1-n的序列建立一棵三叉树,求树上的节点个数

思路:

  • 模拟:加一个递归边界,去掉统计类的无用的a, key, son数组,直接输出节点个数tot就过了
#include<bits/stdc++.h>
using namespace std;

int tot;
void build(int l, int r){
    if(l>r)return ;//RE
    tot = tot+1;
    if(l==r)return ;
    if(r-l+1==1){
        int mid=(l+r)/2;
        build(l,l);
        build(r,r);
    }else{
        int b = l+(r-l+1)/3-1;
        int c = (b+r)/2;
        build(l,b);
        build(b+1,c);
        build(c+1,r);
    }
}

int main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int T;  cin>>T;
    while(T--){
        tot = 0;
        int n;  cin>>n;
        for(int i = 1; i <= n; i++){int x; cin>>x;}
        build(1, n);
        cout<<tot<<"\\n";
    }
    return 0;
}

1007 Banzhuan

题意:

  • 给出一个n,用1*1*1的方块去摆n*n*n的图形,满足从前,左,右,三个方向看去,都是n*n的矩形,每个方块的代价为x*y*y*z,求最小代价。

思路:

  • 贪心:底面铺一层,在左边和前面的边界竖着z轴竖起来两面墙。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e9+7;

//快速幂求逆元
LL pows(LL a, LL x, LL p){if(x==0)return 1; LL t = pows(a, x>>1,p);if(x%2==0)return t*t%p;return t*t%p*a%p;}
LL inv(LL x, LL p){ return pows(x,p-2,p);}

int main(){
    int T;  cin>>T;
    while(T--){
        LL n;  cin>>n;  n%=mod;
        LL a=n*(n+1)%mod*(2*n+1)%mod*inv(6,mod)%mod;//1^2+2^2+...+n^2
        LL b=(2LL+n)%mod*(n-1)%mod*inv(2,mod)%mod;//2+3+...+n
        LL c=((1LL+n)%mod*n%mod*inv(2,mod)%mod+mod)%mod;//1+2+3+...+n
        LL x=(((((a+b)%mod-1)%mod+mod)%mod*b%mod+a*c%mod)%mod+mod)%mod; //x=(a+b-1)*b+a*c;
        LL y=((a*n%mod*c%mod)*n%mod+mod)%mod;//y=(a*n*c)*n;
        cout<<x<<"\\n"<<y<<"\\n";
    }
    return 0;
}

1009 Array

题意:

  • 给出一个长为n的序列,求所有的n*(n+1)/2个子区间中,有多少个区间满足区间内存在某个数大于区间长度的一半。

思路:

Part1

  • 显然对于合法的区间,众数是唯一的,因此不妨枚举众数,将众数标记为1、其余数标记为-1,此时问题即求有多少个区间和大于0
  • 考虑暴力的做法:从左到右枚举右端点,记当前前缀和为sum​​,即查询之前有多少个前缀和小于sum​​​
  • 具体的,记f(i)​​​为(当前)有多少个前缀和为i​​​​,每次的查询累加即为∑[i=min,sum−1]f(i​)​,同时并将f(sum)​​​加1​.(其中min为历史前缀最小值,初始为f0=1且∀i≠0,fi=0)
  • 由于sum​​的变化是连续的,不难线性维护∑[i=min,sum−1]f(i)​​,时间复杂度为o(n^2)​

————
Part2

  • 进一步的,注意到1的段数和是o(n)​的,-1的段数和也是o(n)​的,因此将连续的一段-1一起处理(存的时候存每个众数出现的位置,中间都是-1)
  • 具体的,考虑处理一段-1,假设这段-1之前的前缀和为sum​​​,这段-1的长度为len,即段区间的查询结果为∑[i=sum−len, sum−1]∑[j=min,i−1]f(j)​​​,并将∀sum−len≤i≤sum-1,f(i​​)​加1,
  • 对于修改,可以通过差分维护,并记差分数组为f2(i)​​(定义f(i)=∑[j=min,i]f2(j)​ )
    对于查询,不妨暴力枚举sum,同样利用sum变化的连续性,线性维护∑[j=min,sum]f2(j)和∑[j=min,sum−1]f(j​),那么不难得到查询的结果(sum暴力枚举即代替了i),但时间复杂度仍为o(n^2)

————
Part3

  • 考虑min​,显然当sum≤min时之后的部分必然都为0,不妨直接变为最终的sum−l​(注意更新min)
  • 考虑此时的复杂度,注意到每一次mn减少,都会减少对应sum的枚举,而最终mn至多为所有元素和,因此sum的枚举级别是1的个数,也即o(n)的。对于单个1直接暴力"枚举"sum即可,显然是o(n)的。最终,总复杂度为o(n),可以通过
//AC1
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e6+10;
vector<int>v[maxn];//记录每个众数出现的位置
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int T;  cin>>T;
	while(T--){
		int n;  cin>>n;
		vector<int>a(n+1);   
		unordered_set<int>s; //记录有哪些众数
		for(int i=1; i <= n; i++){
			cin>>a[i];  v[a[i]].push_back(i);  s.insert(a[i]);
		}
		LL ans = 0;
		for(int x : s){//枚举每个数作为众数
			LL res = 0, sum = 0;//x的答案贡献res,当前前缀和sum
			unordered_map<int,int>f1,f2; //前缀和为sum的点有f1[sum]个, f2为f1差分数组
			v[x].push_back(n+1); //防止溢出
			LL k = 0, minn = 0;  //枚举众数的每个位置k,历史前缀最小值minn
			for(int j = 1; j <= n; j++){//枚举每一段-1的起始位置
				if(j > v[x][k])k++;
				if(a[j]!=x && sum==minn){//区间[j,v[x][k]-1]都是-1
					LL len = v[x][k]-1-j;
					f2[sum-len]++; f2[sum+1]--;//区间f1[sum−x,sum-1]+=1
					j += len;
					sum -= len+1;
				}else if(a[j]==x){ //更新答案
					f1[sum]+=f2[sum];//对差分数组求前缀和并更新到f1中去
					f2[sum+1]+=f2[sum];
					f2[sum]=0;
					f1[sum]++;//当前前缀和的个数++
					res += f1[sum];//累加为∑[0,sum−1]f(i​)​
					sum++;
					ans += res;
				}else{
					f1[sum]++; sum--;
					res -= f1[sum];
					ans += res;
				}
				minn = min(minn, sum);
			}
		}
		cout<<ans<<"\\n";
		for(auto &i: s)v[i].clear();
	}
	return 0;
}

//AC2
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e6+10;

LL ans;
vector<int>v[maxn];

int f[maxn<<1], cnt, sum, mn, s;
stack<int>st;
void update(int l, int r){
	f[l]++; f[r+1]--;
	st.push(l), st.push(r+1);
	cnt++;
}
void dec(int x){
	for(int i=0; i < x; i++){
		if(sum==mn){
			sum=mn=sum-(x-i);
			cnt=s=0;
			return ;
		}
		cnt -= f[sum--], s-=cnt, ans+=s;
	}
}

int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T;  cin>>T;
    while(T--){
		int n, m=1e6; ans=0;
		cin>>n;
		for(int i = 0; i <= m; i++)v[i].clear();
		for(int i = 1; i <= n; i++){
			int x;  cin>>x;  v[x].push_back(i);
		}
		for(int i = 0; i <= m; i++){//枚举每个数作为众数 
			int lst = 0;
			sum = mn = n;  cnt=s=0;
			update(sum,sum);
			for(int x : v[i]){//枚举该数的位置
				if(lst+1 < x){//中间都是-1
					dec(x-lst-1);
					update(sum,sum+(x-lst-1)-1);
				}
				s += cnt, cnt += f[++sum], ans += s;
				update(sum,sum);
				lst = x;
			}
			if(lst < n)dec(n-lst);
			while(!st.empty()){
				f[st.top()]=0; st.pop();
			}
		}
		cout<<ans<<"\\n";
    }
    return 0;
}

以上是关于2021杭电多校赛2021“MINIEYE杯”中国大学生算法设计超级联赛签到题4题的主要内容,如果未能解决你的问题,请参考以下文章

2021杭电多校赛2021“MINIEYE杯”中国大学生算法设计超级联赛签到题4题

2021杭电多校赛2021“MINIEYE杯”中国大学生算法设计超级联赛签到题5题

2021杭电多校赛2021“MINIEYE杯”中国大学生算法设计超级联赛签到题5题

2021杭电多校赛2021“MINIEYE杯”中国大学生算法设计超级联赛签到题2题

2021杭电多校赛2021“MINIEYE杯”中国大学生算法设计超级联赛签到题5题

2021杭电多校赛2021“MINIEYE杯”中国大学生算法设计超级联赛签到题4题