喵哈哈村的魔法考试 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\n" , 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\n",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\n" , 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\n" , Answer );
    return 0;
}
posted @ 2017-02-20 10:54  qscqesze  阅读(638)  评论(3编辑  收藏  举报