SMU Summer 2024 Contest Round 1

知识点模块

1.菊花图(n个点)距离为2的点对总数为(n-1)*(n-2)/2;每连接两个距离为2的点对,总的2点对数-1
2.若干个1/2相乘可以写成 w=1循环里w/2

题解模块

Sequence Decomposing
1.题意其实就是要我们找共有多少个最长的上升的子序列,也就是理解成可以找到几个尽量长的队伍(最少LIS不相交覆盖)
2.我们开一个multiset,然后先放进去第一个数,由于multiset会对元素自动从小到大排序,那么我们放进的队尾,也是排序好的,然后从第二个数开始遍历,检查一下当前multiset里是否有比这个数小的数,也就是是否有可以接的队伍,如果有我们就更新这个队伍的队尾为当前遍历到的这个数,如果没有呢,只能自己开一个新的队伍了,也就是直接插入这个值,最后multiset的size就是答案,就是有几个队伍

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
typedef pair<int,int> pii;
int dx[]={0,1,-1,0};
int dy[]={-1,0,0,1};

void solve()
{
    int n;
    cin>>n;
    vector<int>ve(n+1);
    multiset<int>se;//对插入的值自动从小到大排序,且可以重复
    for(int i=1;i<=n;i++) cin>>ve[i];
    se.insert(ve[1]);
    for(int i=2;i<=n;i++)
    {
        auto it=se.lower_bound(ve[i]);//看multiset里面有没有大于该数的,没有it就是ve.end();
        if(it==se.begin())//第一个数就大于ve【i】,也就是说ve【i】无法接到任何一个队伍的后面,只能自己插入当队头
        {
            se.insert(ve[i]);
        }else{//可以找到队伍,it为第一个大于等于该值的下标,则it--为第一个小于该数的下标,也就是可以这个数接的队伍
            it--;
            se.erase(it);//通过删除地址来删除这个值
            se.insert(ve[i]);//更新队伍的队尾
        }
    }
    cout<<se.size();
}


signed main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    int t=1;
    //cin>>t;
    while(t--) solve();
    return 0;
}

Dice and Coin
1.拿样例模拟一下,k=3,则1,2,3各占1/3,1要4次变为16大于10,2要3次,3要2次,那么概率就是1/3*(0.54+0.53+0.52
2.n>k时也不要害怕,大于k的那些数,每个提供的概率就是1/n,枚举计算即可

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
typedef pair<int,int> pii;
int dx[]={0,1,-1,0};
int dy[]={-1,0,0,1};

void solve()
{
    int n,k;
    double sum=0;
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        int x=i;
        double w=1;
        while(x<k)
        {
            x*=2;
            w/=2;//相当于若干个1/2相乘
        }
        sum+=w/n;//每个数字的概率为1/n
    }
    printf("%.12lf",sum);
}


signed main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    int t=1;
    //cin>>t;
    while(t--) solve();
    return 0;
}

Integer Cards
1.贪心的思想,但是注意代码的实现
2.我们用点的方式来存卡牌数和卡牌值,对数组进行从小到大的排序,对点进行第二个值从大到小的排序,在遍历的时候,用一个变量来实现点的下标移动,检查总和加了这个差值是否比原来的总和大,如果更大则说明使用了这个卡牌,对卡牌数减少1,当点的第一个值为0时,下标++

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
typedef pair<int,int> pii;
#define x first
#define y second
#define all(v) v.begin(),v.end()
int dx[]={0,1,-1,0};
int dy[]={-1,0,0,1};
bool cmp(pii a,pii b)
{
    return a.y>b.y;
}
void solve()
{
    int n,m,sum=0;
    cin>>n>>m;
    vector<int>ve(n);
    for(int i=0;i<n;i++)
    {
        cin>>ve[i];
        sum+=ve[i];
    }
    pii p[100005];
    for(int i=0;i<m;i++)
    {
        cin>>p[i].x>>p[i].y;
    }
    sort(all(ve));
    sort(p,p+m,cmp);//记得是p+m,而不是p+n
    int k=0,sum1=sum;
    for(int i=0;i<n;i++)
    {
        if(p[k].x<=0)//点从大到小排序,当这个较大的值用完以后,下标++
        {
            k++;
        }
           sum+=p[k].y-ve[i];
            if(sum1<sum) {//如果加完差值后比原来的大说明合理可以转化
                sum1=sum;
                p[k].x--;//用一次记得减一次
            }
            else break;//因为点是从大到小排的,数组是从小到大排的,如果当前的点无法满足,后续的点当然也无法满足
        
    }
    cout<<sum1;    
}


signed main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    int t=1;
    //cin>>t;
    while(t--) solve();
    return 0;
}

equeue
1.先进行区间的枚举,就是两层循环,比如1 2 3 4 5,定住1,然后右区间起点为2 3 4 5,然后左区间变为1 2,右区间起点为3 4 5以此类推下去
2.当左右区间的长度和大于k时,就要cotinue,因为操作次数无法满足
3.枚举后的区间合法以后,我们把区间的放放到一个新的数组里面,先计算这些数当前的总和,然后对数组进行排序,如果有负数,我们就贪心的减掉这个负数,当然对应消耗一次操作数。然后找最大值即可

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
typedef pair<int,int> pii;
#define x first
#define y second
#define all(v) v.begin(),v.end()
int dx[]={0,1,-1,0};
int dy[]={-1,0,0,1};

void solve()
{
    int n,k,sum=0;
    cin>>n>>k;
    vector<int>ve(n+1);
    for(int i=0;i<n;i++) cin>>ve[i];
    
    for(int l=0;l<n;l++)
    {
        for(int r=l+1;r<=n;r++)
        {
            int x=n-r+l+1;//总共有几个数,左右区间长度的和
           
            int cnt=k-x;
            if(x>k) continue;//区间长度大于操作次数 不符合题意
            vector<int>fi;
            int ans=0;
            for(int i=0;i<=l;i++)  fi.push_back(ve[i]),ans+=ve[i];//注意这里要=l
            for(int i=r;i<n;i++)  fi.push_back(ve[i]),ans+=ve[i];
            sort(all(fi));
           //贪心取出负数,排序后越小的负数排前面 
            for(auto t:fi){
                if(cnt==0) break;
                if(t<0) {
                    ans-=t;
                    cnt--;//操作数记得减1
                }
            }
      
            sum=max(sum,ans);
            
        }
    }
    cout<<sum;
}


signed main()
{
    ios::sync_with_stdio(0),cin.tie(0);
    int t=1;
    //cin>>t;
    while(t--) solve();
    return 0;
}

Friendships
1.首先我们画出一个菊花图(n个点),就是中间一个点,周围被n个点包围,然后连接每个点和这个中间点,我们可以发现一个菊花图距离为2的点对数为(n-2)*(n-1)/2
2.然后随机连接每个距离为2的点,这个菊花图的点对数就会-1,先建图就是(1,2),(1,3).....(1,n),这就表示一个菊花图
3.然后我们枚举连接距离为2的点,比如(2,3),(2,4).....画个图出来会更加直观

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define x first
#define y second
typedef pair<int,int> pii;
#define all(v) v.begin(),v.end()
int dx[]={0,1,-1,0};
int dy[]={-1,0,0,1};
string s;
signed main()
{
 	int n,k;
 	cin>>n>>k;
 	int p=(n-2)*(n-1)/2;//菊花图的点对数
	int cp=p-k;//需要连接的数量
	if(cp<0){
		cout<<-1;
		return 0;
	} 
	vector<pii>ve;
	for(int i=2;i<=n;i++) ve.emplace_back(1,i);
	for(int i=2;i<=n&&cp;i++) //连接次数够了就行 
	{
		for(int j=i+1;j<=n&&cp;j++)
		{
			ve.emplace_back(i,j);
			cp--;
		}
	}
	cout<<ve.size()<<endl;
	for(auto [x,y]:ve) cout<<x<<" "<<y<<endl;
}

posted on 2024-07-08 23:48  swj2529411658  阅读(18)  评论(0编辑  收藏  举报

导航