2021“MINIEYE杯”中国大学生算法设计超级联赛(5)部分题解

C.VC Is All You Need

  • 题意
    给你 n n n个点在 k k k维上,判断你是否可以用 k − 1 k-1 k1维在 2 n 2^n 2n方种着色方案上来分离它们。等价于 k k k维空间中,求 n n n的最大值,使得你可以找到 n n n个点(自己给定坐标),满足:无论对这 n n n个点如何二染色,也就是对于 2 n 2^n 2n种染色方案中的每一种,都总存在一个 k − 1 k-1 k1维超平面,严格分开这两种颜色的点。

  • 解题思路
    k + 1 = n k + 1=n k+1=n时必然可行,我们可以使得 k k k维两侧不同。而当 k + 1 < n k +1<n k+1<n时,无论怎么放置,没有一侧是可以符合的。

  • AC代码

/**
  *@filename:VC_Is_All_You_Need
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-08-03 12:47
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;

int t;
ll n,k;
void solve(){
    if(n - k <= 1){
        cout << "Yes" << endl;
    }
    else{
        cout << "No" << endl;
    }
}
int main(){
    cin >> t;
    while(t -- ){
        cin >> n >> k;
        solve();
    }
    return 0;
}

D.Another String

思路:暴力+双指针
待补

  • std
#include<bits/stdc++.h>
#define dd(x) cerr<<#x<<" = "<<x<<" "
#define de(x) cerr<<#x<<" = "<<x<<endl
#define de_arr(a,s,t) cout<<#a<<": ";for (int z=(s);z<=(t);++z)cout<<a[z]<<" ";cout<<endl;
#define sz(x) int(x.size())
#define All(x) x.begin(),x.end()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef long double ld;
typedef pair<int,int> P;
const double eps=1e-8;
const int mod=1e9+7;
inline int sign(double x){return (x>eps)-(x<-eps);}
inline int add(int a,int b){a+=b;return a<0?a+mod:a>=mod?a-mod:a;}
inline int mul(ll a,ll b){return a*b%mod;}
const int maxn=5e3+10,INF=0x3f3f3f3f;
char s[maxn];
int f[maxn][maxn],cnt[maxn][maxn],pt[maxn];
ll ANS[maxn];
void calc_f(int n,int k){
    for (int st=2;st<=n;++st){
        int dif=0;
        for (int i=1,j=st,len=-1; j<=n; ++i,++j,--len){
            while (i+len+1<=n&&j+len+1<=n&&dif<=k){
                len++; 
                dif+=s[i+len]!=s[j+len];
            }
            f[i][j]=len+(dif<=k);
            dif-=s[i]!=s[j];
        }
    }
}
void solve(int n){
    for (int i=0;i<=n;++i){
        pt[i]=n/2+1;
        for (int j=0;j<=n;++j)
            cnt[i][j]=0;
    }
    ll ans=0;
    for (int j=n;j>1;--j){
        //update
        for (int i=1;i<j;++i)
            while (pt[i]>j-i){
                ans-=cnt[i][pt[i]];
                cnt[i][pt[i]-1]+=cnt[i][pt[i]];
                pt[i]--;
            }
        //add
        for (int i=1;i<j;++i){
            int len=min(f[i][j],j-i);
            cnt[i][len]++;
            ans+=len;
        }
        ANS[j]=ans;
        //delete
        while (pt[j-1]>=0){
            ans-=ll(cnt[j-1][pt[j-1]])*pt[j-1];
            pt[j-1]--;
        }
    }
}
int main()
{
    int T;
    cin>>T;
    while (T--){
        int n,k;
        scanf("%d%d%s",&n,&k,s+1);
        calc_f(n,k);
        solve(n);
        for (int i=2;i<=n;++i)
            printf("%d\n",ANS[i]);
    }
    return 0;
}
 

F.Cute Tree

  • 题意
    给你一段建树函数,计算可以生成几个结点。

  • 解题思路
    按题意写建树函数可行,我们只需在乎区间长度。为了减小递归层数,达到 l o g 3 n log_3n log3n,所以我们需要使用哈希表记忆化信息。

  • AC代码

/**
  *@filename:Cute_Tree
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-08-03 12:13
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;

int t,n;
unordered_map<ll,ll> p;
ll buildTree(int len){
    if(p[len])return p[len];
    if(len == 1){
        return 1;
    }
    if(len == 2){
        return p[len] = buildTree(len / 2) + buildTree(len - len / 2) + 1;
    }
    else{
        if(len % 3 == 0){
            return p[len] = buildTree(len / 3) + buildTree(len / 3) + buildTree(len / 3) + 1;
        }
        else if(len % 3 == 1){
            return p[len] = buildTree(len / 3) + buildTree(len / 3) + buildTree(len / 3 + 1) + 1;
        }
        else{
            return p[len] = buildTree(len / 3 + 1) + buildTree(len / 3) + buildTree(len / 3 + 1) + 1;
        }
    }
}
void solve(){
    printf("%lld\n", buildTree(n));
}
int main(){
    scanf("%d", &t);
    while(t -- ){
        scanf("%d", &n);
        int x;
        for(int i = 1; i <= n; ++ i){
            scanf("%d", &x);
        }
        solve();
    }
    return 0;
}

G.Banzhuan

  • 题意
    给你一个 n × n × n n\times n\times n n×n×n的空 c u b e cube cube,其中我们放置一个单元立方体的代价与它的初始放置位置有关,即若放置在 ( x , y , z ) (x,y,z) (x,y,z)处,那么代价就是 x y 2 z xy^2z xy2z,其中放置的单元体会受重力影响降落直到底下有立方体或者到达底面,计算三视图看过去都是正方形时的代价。

  • 解题思路
    对于最大代价的情况,显然我们需要填充满 n × n × n n \times n \times n n×n×n的空间,并且为了最大化代价,需要将每一砖块从最高处丢下,因此有最大代价: ∑ x = 1 n ∑ y = 1 n ∑ z = 1 n x × y 2 × n = n 2 × ( n + 1 ) n 2 × n ( n + 1 ) ( 2 n + 1 ) 6 \sum_{x=1}^{n}\sum_{y=1}^{n}\sum_{z=1}^{n}x \times y^2 \times n=n^2 \times \frac {(n+1)n}{2} \times \frac {n(n+1)(2n+1)}{6} x=1ny=1nz=1nx×y2×n=n2×2(n+1)n×6n(n+1)(2n+1)
    对于最小代价的情况,显然我们必须要将底面铺平,那么俯视图我们就可以不用管了,对于正视图和左视图,我们要想让这个代价变小,就要使 x , y x,y x,y尽可能小,那么即双方取 1 1 1的时候最小,相当于我们只需要建立好 ( x ≥ 2 , y = 1 ) , ( y ≥ 2 , x = 1 ) (x\geq 2,y=1),(y\geq2,x=1) (x2,y=1),(y2,x=1)的情况即可。即: ∑ x = 1 n ∑ y = 1 n x × y 2 + ∑ x = 2 n ∑ z = 2 n x z + ∑ y = 2 n ∑ z = 2 n y 2 z = n ( n + 1 ) 2 × n ( n + 1 ) ( 2 n + 1 ) 6 + ( n + 2 ) 2 ( n − 1 ) 2 4 + ( n + 2 ) ( n − 1 ) 2 × ( n ( n + 1 ) ( 2 n + 1 ) 6 − 1 ) \sum_{x=1}^{n}\sum_{y=1}^{n}x \times y^2+\sum_{x=2}^n\sum_{z=2}^nxz+\sum_{y=2}^n\sum_{z=2}^ny^2z=\frac{n(n+1)}{2}\times \frac{n(n+1)(2n+1)}{6}+\frac{(n+2)^2(n-1)^2}{4}+\frac{(n+2)(n-1)}{2}\times (\frac{n(n+1)(2n+1)}{6}-1) x=1ny=1nx×y2+x=2nz=2nxz+y=2nz=2ny2z=2n(n+1)×6n(n+1)(2n+1)+4(n+2)2(n1)2+2(n+2)(n1)×(6n(n+1)(2n+1)1)

  • AC代码

/**
  *@filename:banzhuan
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-08-03 12:58
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100000 + 5;
const ll P = 1e9+7;

int t;
ll n;
ll qsm(ll n, ll q){
    ll ans = 1;
    while(q){
        if(q & 1)ans = ans % P * n % P;
        n = n % P * n % P;
        q >>= 1;
    }
    return ans;
}
void solve(){
    n %= P;
    //a is n * (n + 1) / 2, b is n * (n + 1) * (2 * n + 1) / 6
    ll a = n * (n + 1) % P * qsm(2,P - 2) % P, 
    b = n * (n + 1) % P * (2 * n + 1) % P * qsm(6,P - 2) % P;
    cout << (a * b % P + (a - 1) * (a - 1) % P + (b - 1) * (a - 1) % P) % P << endl;
    cout << a * b % P * (n % P) % P * (n % P) % P << endl;//max
}
int main(){
    cin >> t;
    while(t -- ){
        cin >> n;
        solve();
    }
    return 0;
}

I.Array

思路:线段树/树状数组+差分
待补

  • std
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
const int N=2e6+3;
vector<int>G[N];
int main(){
	ios::sync_with_stdio(0);cin.tie(0);
	int n,t;
	using namespace myFuction;
	print();
	cin>>t;
	while(t--){
		cin>>n;
		unordered_set<int>s;
		vector<int>a(n+1);
		for(int i=1;i<=n;++i){
			cin>>a[i]; s.insert(a[i]); G[a[i]].pb(i);
		}
		ll ans=0;
		for(auto num:s){ // 枚举每个数作为众数 
			ll res=0;// 答案 
			ll sum=0;// 当前前缀和
			unordered_map<int,int>f1,f2;// 前缀和为sum的点有f1[sum]个 
			G[num].pb(n+1);
			ll k=0,minn=0;
			for(int j=1;j<=n;++j){
				if(j>G[num][k]) k++;
				if(a[j]!=num&&sum==minn){
					ll len=G[num][k]-j-1;
					f2[sum+1]--;
					f2[sum-len]++;
					j+=len;
					sum-=len+1;
				}
				else if (a[j]==num){
					f1[sum]+=f2[sum];
					f2[sum+1]+=f2[sum];
					f2[sum]=0;
					f1[sum]++;
					res+=f1[sum];
					sum++;
					ans+=res;
				}
				else{
					f1[sum]++;
					sum--;
					res-=f1[sum];
					ans+=res;
				}
				if(minn>sum)minn=sum;
			}
			
		}
		cout<<ans<<'\n';
		for(auto &i:s)G[i].clear();
	}
	return 0;
}
posted @   unique_pursuit  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示