zoukankan      html  css  js  c++  java
  • 喵哈哈村的魔法考试 Round #1 (Div.2) 题解

    喵哈哈村的魔法考试 Round #1 (Div.2) 题解

    特别感谢出题人,qscqesze。

    也特别感谢测题人Xiper和CS_LYJ1997。

    没有他们的付出,就不会有这场比赛。

    A 喵哈哈村的魔法石

    暴力(qscqesze):观察数据范围,显然最多10000个A,10000个B,所以最暴力的做法就是两个for循环,一个forA,一个forB,复杂度就是O(10000*10000),这个出题人因为是div2 A,所以就直接放过了的。

    机智的做法(CS_LYJ1997):在暴力的基础上优化,第二维我们根本不需要枚举。我们直接O(1)判断是否成立就好了嘛。

    #include<bits/stdc++.h>
    using namespace std;
    
    int main()
    {
        //freopen("4.in","r",stdin);
        //freopen("4.out","w",stdout);
        int t;
        scanf("%d",&t);
        while(t--){
            long long a,b,c;
            cin>>a>>b>>c;
            int flag = 0;
            for(int i=0;i<=100000;i++)
            {
                long long res = c - a*i;
                if(res==0){
                    puts("Yes");
                    flag = 1;
                    break;
                }
                if(res<0)break;
                long long p = res/b;
                if(p*b==res)
                {
                    flag = 1;
                    puts("Yes");
                    break;
                }
            }
            if(flag==0)
                puts("No");
        }
    
    }
    

    愚蠢的做法(Xiper):显然Ax+By=C,这个就是个exgcd的板子题,但是要讨论一下整数的问题,所以大力讨论一发就好了嘛。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    pair < int , int > Extend_GCD( int a , int b , int g ){
    	if( b == 0 )
    		return make_pair( g / a , 0 );
    	else{
    		pair < int , int > ret = Extend_GCD( b , a % b , g );
    		return make_pair( ret.second , ret.first - a / b * ret.second );
    	}
    }
    
    int GetRangel( int Up , int Down ){
    	if( Up >= 0 )
    		return Up / Down;
    	if( abs( Up ) % Down == 0 )
    		return Up / Down;
    	return Up / Down - 1;
    }
    
    int GetRanger( int Up , int Down ){
    	if( Up >= 0 )
    		return (Up + Down - 1) / Down;
    	return Up / Down;
    }
    
    
    int main( int argc , char * argv[] ){
    	int T;
    	scanf( "%d" , & T );
    	while( T -- ){
    		int a , b , c , gcd;
    		scanf( "%d%d%d" , & a , & b , & c );
    		gcd = __gcd( a , b );
    		if( c % gcd )
    			puts( "No" );
    		else{
    			pair < int , int > Sol = Extend_GCD( a , b , c );
    			int ranger = GetRanger( - gcd * Sol.first , b );
    			int rangel = GetRangel( gcd * Sol.second , a );
    			if( rangel >= ranger )
    				puts( "Yes" );
    			else
    				puts( "No" );
    		}
    	}
    	return 0;
    }
    

    B 喵哈哈村的括号序列

    正确题解(qscqesze,xiper):这是一道栈+dp的综合运用的题目。如果我们遇到(,那么我们就把这个位置放进栈里面。如果遇到)的话,就pop掉栈顶,且栈顶就是这个)匹配的(位置,如果我们用l[i]来表示与i匹配的左括号位置的话。

    那么我们令dp[i]表示以i结尾的括号序列的最长长度的方程为:dp[i]=dp[l[i]-1]+(i-l[i]+1)。

    最后在所有的dp里面取个max就好了。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5+7;
    string s;
    stack<int> k;
    int dp[maxn];
    void solve(){
        memset(dp,0,sizeof(dp));
        while(k.size())k.pop();
        cin>>s;
        for(int i=0;i<s.size();i++)
        {
            if(s[i]=='(')
                k.push(i);
            else
            {
                if(!k.empty())
                {
                    dp[i]=i-k.top()+1;
                    if(k.top()>0)
                        dp[i]+=dp[k.top()-1];
                    k.pop();
                }
            }
        }
        int ans1=0;
        for(int i=0;i<s.size();i++)
            if(dp[i]>ans1)
                ans1=dp[i];
        cout<<ans1<<endl;
    }
    int main(){
        int t;
        scanf("%d",&t);
        while(t--)
            solve();
    }
    

    C 喵哈哈村的魔法石(II)

    题解(qscqesze):一道背包dp,原题来自于atcoder的一场beginner的比赛D题。

    dp[i][j]表示能量用了i的人力,j的地力的最小花费。

    那么显然就是dp[i][j] = min(dp[i][j],dp[i-a[i]][j-b[i]]+c[i])。

    注意,这是一个01背包,所以你得倒着枚举这个状态,以保证状态不会覆盖。(或者你直接三维状态表示也可以。)

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN = 1005;
    int dp[MAXN][MAXN],n,ma,mb,a[MAXN],b[MAXN],c[MAXN];
    void init(){
        for(int i=0;i<MAXN;i++)
            for(int j=0;j<MAXN;j++)
                dp[i][j]=1<<30;
        dp[0][0]=0;
    }
    int main(){
        //freopen("2.in","r",stdin);
        //freopen("2.out","w",stdout);
        int t;
        scanf("%d",&t);
        while(t--){
            init();
            scanf("%d%d",&n,&ma);
            for(int i=0;i<n;i++)
                scanf("%d%d%d",&a[i],&b[i],&c[i]);
            for(int i=0;i<n;i++){
                for(int j=MAXN-1;j>=0;j--){
                    for(int k=MAXN-1;k>=0;k--){
                        if(j>=a[i]&&k>=b[i]){
                            dp[j][k]=min(dp[j-a[i]][k-b[i]]+c[i],dp[j][k]);
                        }
                    }
                }
            }
            int Ans = 1<<30;
            for(int i=1;i<MAXN;i++){
                for(int j=1;j<MAXN;j++){
                    if(i%j==0&&i/j==ma){
                        Ans = min(Ans,dp[i][j]);
                    }
                }
            }
            if(Ans!=1<<30)
                cout<<Ans<<endl;
            else
                cout<<"ShenBaoBao GG"<<endl;
        }
    }
    

    D 喵哈哈村的赛马比赛

    原题是2017网易雷火游戏研发部门笔试题最后一题。

    正常做法(Xiper):算每头马对答案的贡献,不妨设有f(i),考虑一个排列就是他们之间的速度关系,第i头马对答案有贡献当且仅当前面的马速度都比他小,那么方案数就是((i-1)!(n-i)!C(n,i))/(n!),化简之后,就是1/i。

    更加通俗的语言(CS_LYJ1997):最后一匹马肯定会留下来,所以为1,倒数第二匹马要留下来,得比最后一匹马速度快,就是1/2,最前面的马得是n匹马中最快的才能留下来,就是1/n。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    
    int main( int argc , char * argv[] ){
    	int T;
    	scanf( "%d" , & T );
    	while( T -- ){
    		int n ;
    		scanf( "%d" , & n );
    		double Answer = 0;
    		for(int i = 1 ; i <= n ; ++ i)
    			Answer += 1.0 / ( double ) i;
    		printf( "%.4lf
    " , Answer );
    	}
    	return 0;
    }
    

    智障做法(qscqesze):把这道题分为两个部分,A为所有情况最后马匹剩下来的和,B为总方案数。B显然就是n!,A我们打个表,然后去找规律(oeis),发现是一个递推的式子。然后我们掏出java写个高精度就完了嘛。

    import java.util.*;
    import java.math.*;
    
    public class Main {
    
        static int n, t;
        static BigDecimal a[]=new BigDecimal[1002];
        static BigDecimal b[]=new BigDecimal[1002];
        static double c[] = new double[1002];
        
        static public void main(String args[]) {
            Scanner IN=new Scanner(System.in);
            b[0]=BigDecimal.valueOf(1);
            a[0]=BigDecimal.valueOf(0);
            for(int i=1;i<=1000;i++)
            	b[i]=b[i-1].multiply(BigDecimal.valueOf(i));
            for(int i=1;i<=1000;i++)
            {
            	a[i]=a[i-1].multiply(BigDecimal.valueOf(i));
            	a[i]=a[i].add(b[i-1]);
            }
            t=IN.nextInt();
            for(int cas=1;cas<=t;cas++){
                n=IN.nextInt();
                double l = 0,r = 10;
                for(int i=0;i<50;i++){
                	double mid = (l+r)/2.0;
                	if(b[n].multiply(BigDecimal.valueOf(mid)).compareTo(a[n])>=0){
                		r=mid;
                	}else
                		l=mid;
                }
                String double_str = String.format("%.4f", l);
                System.out.println(double_str);
            }
        }
    }
    

    E 喵哈哈村的海报问题

    该题目是qscqesze在大一的时候出的,但是由于当时年幼无知,标程写错了,现在掏出来改了一下标程,然后就扔了出来。

    题解1(qscqesze):

    首先离散化。然后我们将所有的操作按照时间倒序排序,即t大的在前面,t小的在后面。那么显然如果这个节点被操作了之后,就不会再被更新了。

    暴力显然就是O(nmk)的复杂度。

    于是我们用并查集来优化一下,每一行,我们暴力处理。将fa指向每一行下一个未操作的格子。初始化是指向自己的。操作完之后,就指向下一个。这样的话,由于每个格子就只会访问一次,那么复杂度就变为了O(nmα(k)+mk),就过了。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 2005;
    struct node
    {
        int x1,y1,x2,y2,t,c;
    };
    int n,m,c;
    int k;
    node a[maxn];
    vector<int> q1;
    vector<int> q2;
    map<int,int> H1;
    map<int,int> H2;
    int fa[2010][2010];
    int mp[2010][2010];
    map<int,int> flag;
    int ans;
    
    bool cmp(node b,node c)
    {
        return b.t>c.t;
    }
    
    int fi(int x,int y)
    {
        if(y!=fa[x][y])
            fa[x][y]=fi(x,fa[x][y]);
        return fa[x][y];
    }
    
    int main()
    {
        scanf("%d%d%d",&n,&m,&c);
        q1.push_back(n);
        q2.push_back(m);
        q1.push_back(1);
        q2.push_back(1);
        scanf("%d",&k);
        for(int i=0;i<k;i++)
        {
            scanf("%d%d%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2,&a[i].c,&a[i].t);
            q1.push_back(a[i].x1);
            q1.push_back(a[i].x2);
            q2.push_back(a[i].y1);
            q2.push_back(a[i].y2);
        }
        a[k].x1=1,a[k].y1=1,a[k].x2=n,a[k].y2=m,a[k].c=c,a[k].t=0;
    
        sort(a,a+k+1,cmp);
    
        sort(q1.begin(),q1.end());
        sort(q2.begin(),q2.end());
        q1.erase(unique(q1.begin(),q1.end()),q1.end());
        q2.erase(unique(q2.begin(),q2.end()),q2.end());
    
        for(int i=0;i<q1.size();i++)
            H1[q1[i]]=i+1;
        for(int i=0;i<q2.size();i++)
            H2[q2[i]]=i+1;
    
        for(int i=1;i<=q1.size()+3;i++)
            for(int j=1;j<=q2.size()+3;j++)
                fa[i][j]=j;
    
        for(int p=0;p<=k;p++)
        {
            for(int i=H1[a[p].x1];i<=H1[a[p].x2];i++)
            {
                int now = H2[a[p].y1];
                while(now<=H2[a[p].y2]){
                    if(!mp[i][now]){
                        mp[i][now]=a[p].c;
                        if(!flag[a[p].c])
                            ans++;
                        flag[a[p].c]=1;
                    }
                    fa[i][now]=now+1;
                    now = fi(i,now);
                }
            }
        }
        printf("%d
    ",ans);
    }
    

    题解2(xiper):修改操作等价于是二维区间的每个点取MAX,MAX的第一维是时间,第二维是颜色,离线下来,对于第一维从左到右扫,维护好现在的Y上面的线段,每个每个X,再对Y离线,开始搞。两个离线暴力搞。

    Xiper说还有Klog的做法,但是他现在一直没抠出来,还在僵硬中。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int maxn = 4000 + 10;
    
    int n , m , c , occurance[maxn] , Answer ;
    
    struct Operation{
    	int l , r , tp ;
    	pair < int , int > value;
    	Operation( int l , int r , int tp , pair < int , int > value ):
    		l( l ) , r( r ) , tp( tp ) , value( value ){}
    };
    
    struct Line{
    	int l , r ;
    	pair < int , int > value;
    	Line( int l , int r , pair < int , int > value) : l ( l ) , r ( r ) , value( value ){}
    
    	friend bool operator < (const Line & a , const Line & b){
    		return a.l < b.l || (a.l == b.l && a.r < b.r) || (a.l == b.l && a.r == b.r && a.value < b.value);
    	}
    };
    
    set < Line > li;
    priority_queue < pair < int , int > > os;
    short cnt[1005][1005],inq[1005][1005];
    map < int , vector < Operation > > seq;
    vector < int > sx , sc , sy , st;
    vector < pair < int , int > > Add[maxn] , Del[maxn];
    
    int main( int argc , char * argv[] ){
    	scanf( "%d%d%d" , & n , & m , & c );
    	seq[1].push_back( Operation( 1 , m , 1 , make_pair( 0 , c ) ) );
    	seq[n].push_back( Operation( 1 , m , -1 , make_pair( 0 , c ) ) );
    	int K;
    	scanf( "%d" , & K );
    	for(int i = 1 ; i <= K ; ++ i){
    		int x1 , y1 , x2 , y2 , colour , time ;
    		scanf( "%d%d%d%d%d%d" , & x1 , & y1 , & x2 , & y2 , & colour , & time );
    		seq[x1].push_back( Operation( y1 , y2 , 1 , make_pair( time , colour ) ) );
    		seq[x2].push_back( Operation( y1 , y2 , -1 , make_pair( time , colour ) ) );
    	}
    	for(auto & ir : seq)
    		for(auto & it : ir.second )
    			sc.push_back( it.value.second ) ,
    			st.push_back( it.value.first ) ,
    			sy.push_back( it.l ) ,
    			sy.push_back( it.r ) ;
    	sort( sc.begin() , sc.end() );
    	sort( st.begin() , st.end() );
    	sort( sy.begin() , sy.end() );
    	int sclen = unique( sc.begin() , sc.end() ) - sc.begin();
    	int sylen = unique( sy.begin() , sy.end() ) - sy.begin();
    	int stlen = unique( st.begin() , st.end() ) - st.begin();
    	for(auto & ir : seq)
    		for(auto & it : ir.second )
    			it.value.second = lower_bound( sc.begin() , sc.begin() + sclen , it.value.second ) - sc.begin(),
    			it.value.first = lower_bound( st.begin() , st.begin() + stlen , it.value.first ) - st.begin(),
    			it.l = lower_bound( sy.begin() , sy.begin() + sylen , it.l ) - sy.begin(),
    			it.r = lower_bound( sy.begin() , sy.begin() + sylen , it.r ) - sy.begin();
    	for(auto & ir : seq){
    		int x = ir.first;
    		auto & f = ir.second;
    		for(auto it : f)
    			if( it.tp == 1 )
    				li.insert( Line( it.l , it.r , it.value ) );
    		for(int i = 0 ; i < sylen ; ++ i)
    			Add[i].clear(),
    			Del[i].clear();
    		for(auto it : li){
    			Add[it.l].push_back( it.value );
    			Del[it.r].push_back( it.value );
    		}
    		for(int i = 0 ; i < sylen ; ++ i){
    			for(auto it : Add[i]){
    				if(!inq[it.first][it.second]){
    					inq[it.first][it.second] = 1;
    					os.push( it );
    				}
    				++ cnt[it.first][it.second];
    			}
    			while( !os.empty() ){
    				pair < int , int > vq = os.top();
    				if( !cnt[vq.first][vq.second] ){
    					inq[vq.first][vq.second] = 0;
    					os.pop();
    				}else
    					break;
    			}
    			if( !os.empty() )
    				occurance[os.top().second] = 1;
    			for(auto it : Del[i])
    				-- cnt[it.first][it.second];
    		}
    		while( !os.empty() ){
    			pair < int , int > vq = os.top();
    			cnt[vq.first][vq.second] = inq[vq.first][vq.second] = 0;
    			os.pop();
    		}
    		for(auto it : f)
    			if( it.tp == -1 )
    				li.erase( Line( it.l , it.r , it.value ) );
    	}
    	for(int i = 0 ; i < sclen ; ++ i)
    		if( occurance[i] )
    			++ Answer;
    	printf( "%d
    " , Answer );
    	return 0;
    }
    

    E题Xiper最后做出来了,代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    inline int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
    const int maxn = 2e5 + 50;
    int n , m , c , K ;
    
    struct Operation{
        int x , y1 , y2 , colour , time , tp;
        Operation( int x , int y1 , int y2 , int colour , int time , int tp ) :
            x( x ) , y1( y1 ) , y2( y2 ) , colour( colour ) , time( time ) , tp( tp ){}
        friend bool operator <  ( const Operation & a , const Operation & b ){
            return a.x < b.x || ( a.x == b.x && a.tp > b.tp );
        }
    };
    
    vector < Operation > seq;
    vector < int > sy , sc , st ;
    int sylen , sclen , stlen , occurance[maxn];
    
    struct SegmentTree{
        struct Node{
            int l , r , lazy , mi , mxtime , mxcolour;
            set < pair < int , int > > s;
            void Cal( int y ){
                if( !s.empty() && mxtime >= mi && mxtime >= y )
                    occurance[mxcolour] = 1;
                if( lazy == -2 )
                    lazy = max( y , mxtime );
                else
                    lazy = min( lazy , max( y , mxtime ) );
            }
    
            void Update(){
                if( s.empty() )
                    mxtime = mxcolour = -1;
                else{
                    pair < int , int > last = *(--s.end());
                    mxtime = last.first , mxcolour = last.second;
                }
            }
    
        }tree[maxn << 2];
    
        void Build( int l , int r , int o ){
            tree[o].l = l , tree[o].r = r , tree[o].lazy = -2 , tree[o].mi = -1;
            if( r - l > 1 ){
                int mid = l + r >> 1;
                Build( l , mid , o << 1 );
                Build( mid , r , o << 1 | 1 );
            }
        }
    
        void ReLeaseLabel( int o ){
            if( tree[o].r - tree[o].l > 1 && tree[o].lazy != -2 ){
                tree[o << 1].Cal( tree[o].lazy );
                tree[o << 1 | 1].Cal( tree[o].lazy );
                tree[o].lazy = -2;
            }
        }
    
        void Maintain( int o ){
            int l = tree[o].l , r = tree[o].r ;
            tree[o].mi = tree[o].mxtime;
            if( r - l > 1 )
                tree[o].mi = max( min( tree[o << 1].mi , tree[o << 1 | 1].mi ) , tree[o].mxtime );
        }
    
        void Modify( int ql , int qr , pair < int , int > UpdValue , int o ){
            int l = tree[o].l , r = tree[o].r;
            ReLeaseLabel( o );
            if( ql <= l && r <= qr ){
                tree[o].s.insert( UpdValue );
                tree[o].Update();
            }
            else{
                int mid = l + r >> 1;
                if( ql < mid )
                    Modify( ql , qr , UpdValue , o << 1 );
                if( qr > mid )
                    Modify( ql , qr , UpdValue , o << 1 | 1 );
            }
            Maintain( o );
        }
    
        void Delete( int ql , int qr , pair < int , int > DeleteValue , int o ){
            int l = tree[o].l , r = tree[o].r;
            ReLeaseLabel( o );
            if( ql <= l && r <= qr ){
                tree[o].s.erase( DeleteValue );
                tree[o].Update();
            }
            else{
                int mid = l + r >> 1;
                if( ql < mid )
                    Delete( ql , qr , DeleteValue , o << 1 );
                if( qr > mid )
                    Delete( ql , qr , DeleteValue , o << 1 | 1 );
            }
            Maintain( o );
        }
    
    }SegmentTree;
    
    int getrank( int x , vector < int > & sx , int slen ){
        return lower_bound( sx.begin() , sx.begin() + slen , x ) - sx.begin();
    }
    
    int main( int argc , char * argv[] ){
        n = read() , m = read() , c = read() , K = read();
        seq.push_back( Operation( 1 , 1 , m + 1 , c , 0 , 1 ) );
        seq.push_back( Operation( n , 1 , m + 1 , c , 0 , -1 ) );
        for(int i = 1 ; i <= K ; ++ i){
            int x1 = read() , y1 = read() , x2 = read() , y2 = read() , c = read() , t = read();
            seq.push_back( Operation( x1 , y1 , y2 + 1 , c , t , 1 ) );
            seq.push_back( Operation( x2 , y1 , y2 + 1 , c , t , -1 ) );
        }
        for(auto & it : seq)
            sy.push_back( it.y1 ) ,
            sy.push_back( it.y2 ) ,
            sc.push_back( it.colour ) ,
            st.push_back( it.time ) ;
        sort( sy.begin() , sy.end() );
        sort( sc.begin() , sc.end() );
        sort( st.begin() , st.end() );
        sylen = unique( sy.begin() , sy.end() ) - sy.begin();
        sclen = unique( sc.begin() , sc.end() ) - sc.begin();
        stlen = unique( st.begin() , st.end() ) - st.begin();
        for(auto & it : seq)
            it.y1 = getrank( it.y1 , sy , sylen ) ,
            it.y2 = getrank( it.y2 , sy , sylen ) ,
            it.colour = getrank( it.colour , sc , sclen ) ,
            it.time = getrank( it.time , st , stlen ) ;
        SegmentTree.Build( 0 , sylen - 1 , 1 );
        sort( seq.begin() , seq.end() );
        for(int i = 0 ; i < seq.size() ; ){
            int j = i , check = 0;
            while( j < seq.size() && seq[j].x == seq[i].x ){
                auto & x = seq[j];
                if( x.tp > 0 )
                    SegmentTree.Modify( x.y1 , x.y2 , make_pair( x.time , x.colour ) , 1 );
                else{
                    if(!check)
                        SegmentTree.tree[1].Cal( -1 ) ,
                        check = 1;
                    SegmentTree.Delete( x.y1 , x.y2 , make_pair( x.time , x.colour ) , 1 );
                }
    
                ++ j;
            }
    
    
            if( j != seq.size() && j > 0 && seq[j].x - seq[j - 1].x > 1 )
                SegmentTree.tree[1].Cal( -1 ) ;
            i = j;
        }
        int Answer = 0;
        for(int i = 0 ; i < sclen ; ++ i)
            if( occurance[i] )
                ++ Answer;
        printf( "%d
    " , Answer );
        return 0;
    }
  • 相关阅读:
    [Hibernate]
    [Hibernate]
    [Hibernate]
    [Hibernate]
    [Hibernate]
    [Hibernate]
    [Hibernate]
    [Hibernate]
    [Hibernate]
    [Hibernate]
  • 原文地址:https://www.cnblogs.com/qscqesze/p/6418555.html
Copyright © 2011-2022 走看看