校内团队训练赛2

校内团队训练赛2

总结:太惨了,卡水题卡两小时,直接飞了。

题目如下:

Similar Arrays

[传送门]:

题意:给n表示数组长度,m个关系,每个关系两个索引,表示这两个索引之间的值必须满足大于或小于关系。然后构造两个数组,第一个数组是1到n的排列,第二个数组至少有一对数值相等,两个数组都要满足所有关系,能构造输出方案,不能输出no。

分析:主要还是不太会这种,题目的要求有点抽象,感觉情况挺多的很多种答案,当时就想bfs贪心,利用关系建图,然后让深度为偶数的尽量从1开始往上取,为奇数的从n开始往下取,因为感觉这样不会导致出现连环关系,比如a>b,b>c的话,那a一定大于c,那第二个数组就不能让a和c相等了,所以想尽量一大一小的构造,然后有挺多bug的,如a,b,c间两两存在关系,即使构造成小大小,那a和c间的关系还是定下来了,因为是bfs赋值给c的,所以c会大于a,但是在bfs的过程中,vis[a]已经点亮了,所以不会再从c走回a,原本不能相等的a和c被我判可以相等了,然后改了好几次还是有bug,根本就不是图论题。

题解:任选两个没得关系的数变成一样的n,其他数从小到大赋值成1到n-2,我人傻了,给大一没学过bfs的我,这题估计10多分钟就能想出来,那天比赛卡了我2个钟头。后来想了下确实很合理,其他数什么关系不用管,先完成至少一组数相等的任务,把没有关系的数变成最大其他数就从小到大,a数组从小到大,b数组也是那关系肯定都是一致的。

代码:

    #include<iostream>
    #include<map>
    using namespace std;
    #define ll long long
    ll n,m,u,v,ans[100007];
    map<pair<ll,ll> ,ll>ma;
     
    int main(){
    	int ok=0;
    	scanf("%lld%lld",&n,&m);
    	for(int i=1;i<=m;i++){
    		scanf("%lld%lld",&u,&v);
    		ma[{u,v}]=1;
    		ma[{v,u}]=1;
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=i+1;j<=n;j++){
    			if(ma[{i,j}]==0){
    				ok=1;
    				ans[i]=n;
    				ans[j]=n;
    			}
    			if(ok==1)break;
    		}
    		if(ok==1)break;
    	}
    	if(ok==0)printf("NO
");
    	else{
    		printf("YES
");
    		int cnt=1;
    		for(int i=1;i<=n;i++){
    			if(ans[i]==0){
    				ans[i]=cnt++;
    			}
    		}
    		int lin=0;
    		for(int i=1;i<=n;i++){
    			if(ans[i]==n){
    				printf("%lld ",ans[i]-lin);
    				lin=1;
    			}
    			else printf("%lld ",ans[i]);
    		}
    		printf("
");
    		for(int i=1;i<=n;i++){
    			printf("%lld ",ans[i]);
    		}
    		printf("
");
    	}
    }

Berland University

[传送门]:

题意:输入t,n,a,b,k。t表示总人数,n表示天数,a表示奇数天最多多少人上课,b表示偶数天多少人上课,k表示一个人至少上k节课及格,求最多多少人及格。

题解:后面没什么时间想了,被水题卡的大脑短路,关键这题要想到的是上课的最优策略,因为有限制,每人最多一天只能上一节课,如果一批人先把前几天所有课抢了上满以后,可能会导致后面天数不够,空有教室座位而没有天数了,那么,一天要怎么上最优。只要别让一批人先上完前几天的课就行了,让他们一个一个上课,如果你想让q个人及格,即没有q人全上完第一节课,就不能让上过第一节课的人去上第二节课,这样可能会导致q个人全部挂科,但不重要,因为这说明,无法满足让q人全及格,而我只要找到符合要求的最大的q就行了,很明显的二分。

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
#define ll long long
ll t,n,a,b,k,ans;
 
ll ok(ll m){
    ll ans1=min(a,m)*((n+1)/2);
    ll ans2=min(b,m)*(n/2);
    if((ans1+ans2)/k>=m){
        return 1;
    }
    else{
        return 0;
    }
}
 
int main(){
    scanf("%lld%lld%lld%lld%lld",&t,&n,&a,&b,&k);
    ll l=1,r=t,mid;
    while(r>=l){
        mid=(l+r)/2;
        if(ok(mid)){
            ans=mid;
            l=mid+1;
        }
        else{
            r=mid-1;
        }
    }
    printf("%lld
",ans);
}

Minimal Product

[传送门]:

题意:求出最小的a[i]*a[j]满足i<j,a[i]<a[j],但先用给你的公式算出b数组,并对2^32取模,再用b数组算出a数组;

题解:题目比较特殊,一般都是对1e9+7取模,这个是对2^32取模,从学长得知,unsigned int 可以自动膜2^32,不用这个的话两个这么大的数相乘会爆long long int,然后算出a数组后,可以从前往后贪心记录最小值并,用当前位置以前的最小值和当前的数相乘,更新答案,但是有个bug,如-3,-2,-1最优值应该是-2,但这样贪心的答案变成-3了,原理是因为负负得正,这里我们想到再从后往前的走一边,相反我们记录最大值,hack数据就通过了。

    #include<iostream>
    #include<queue>
    using namespace std;
    #define ll long long
    static ll maxn=5e18+7;
    ll t,n,l,r;
    unsigned int b[10000007],x,y,z;
    ll a[10000007],minn,ans;
    int main(){
        scanf("%lld",&t);
        while(t--){
            scanf("%lld%lld%lld%d%d%d%d%d",&n,&l,&r,&x,&y,&z,&b[1],&b[2]);
            for(int i=3;i<=n;i++){
                b[i]=b[i-2]*x+b[i-1]*y+z;
            }
            ans=maxn;
            minn=maxn;
            for(int i=1;i<=n;i++){
                a[i]=b[i]%(r-l+1);
                a[i]+=l;
                if(a[i]>minn){
                    ans=min(ans,a[i]*minn);
                }
                else{
                    minn=a[i];
                }
            }
            minn=-maxn;
            for(int i=n;i>=1;i--){
                if(a[i]<minn){
                    ans=min(ans,a[i]*minn);
                }
                else{
                    minn=a[i];
                }
            }
            if(ans==maxn){
                printf("IMPOSSIBLE
");
            }
            else{
                printf("%lld
",ans);
            }
        }
    }

Right Expansion Of The Mind

[传送门]:

题意:n组,每组两个字符串,一个表示前缀,一个表示后缀,其中后缀是无限循环的,例如

前缀:ab;后缀:ac,那么这组字符串就表示为:abacacacacaca...,然后将这n组归类,互为另一个字符串的子序列的可以归为一类,然后要使类的数量最少,输出归类方案。

题解:这题感觉起来还是蛮麻烦的,我也想了有一段时间,判断是不是子序列就有点头痛了,

一类还要两两互为子序列。后发现要记录后缀的字母种类数,因为后缀是无限的约等于各字母无限数量可以随意排序,还有后缀必须包含前缀的所有字母才能与其他组归为一类,因为如果当前组字符串可表示为另一组的子序列说明它的后缀要有当前组前缀的字母与后缀的字母才行,那么它又要是当前组的子序列,那当前的后缀起码要有它后缀的所有字母才行,及后缀必须包含前缀的所有字母。但是写到一半发现了bug,如:(ab,c),(ab,cc)这两组可以归为一类,即使后缀不包含前缀,原因是前缀刚好抵消了,同时(abcz,cz),(ab,cz)也可以归为一类,所以真的答案是用后缀将前缀的末尾包含后缀的字母的部分去掉,同时满足前缀相等,后缀字母种类相等的可以归为一类,模拟的话我用的map加vector感觉这样记录方便点。

    #include<iostream>
    #include<vector>
    #include<map>
    #include<cstring>
    using namespace std;
    int n,a[100007],er[107];
    string s1,s2;
    vector<int>lose;
    map<pair<string,int> ,vector<int> >ma;
    void go(int p,string s1,string s2){
        int lin;
        for(int i=0;i<s2.length();i++){
                lin=s2[i]-'a';
                if(!(a[p]&er[lin])){
                    a[p]+=er[lin];
                }
        }
        while(1){
            lin=s1[s1.length()-1]-'a';
            if((er[lin]&a[p])){
                s1.pop_back();
            }
            else{
                break;
            }
        }
        ma[{s1,a[p]}].push_back(p);
    }
    int main(){
        scanf("%d",&n);
        er[0]=1;
        for(int i=1;i<=26;i++){
            er[i]=2*er[i-1];
        }
        for(int i=1;i<=n;i++){
            cin>>s1>>s2;
            go(i,s1,s2);
        }
        map<pair<string,int> ,vector<int> >::reverse_iterator   iter;
        printf("%d
",ma.size());
        for(iter = ma.rbegin(); iter != ma.rend(); iter++){
        	printf("%d ",iter->second.size());
        	for(int i=0;i<iter->second.size();i++){
                printf("%d ",iter->second[i]);
        	}
        	printf("
");
        }
    }
原文地址:https://www.cnblogs.com/whitelily/p/13228144.html