2018-2019 XIX Open Cup, Grand Prix of Korea (Division 2)

A.跟算法课上的活动安排问题的贪心策略一样,唯一的区别就是加了拓扑序。

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long LL;
const LL inf = 0x3f3f3f3f3f3f3f3f;
const LL maxn= 300000 + 10;
typedef pair<LL,LL> pii;
typedef pair<pii,LL> piii;
pii a[maxn];
vector<LL> ve1[maxn],ve2[maxn],ans;
LL du1[maxn],cnt,du2[maxn],n;
void init(){
    memset( du1,0,sizeof( du1 ) );
    memset( du2,0,sizeof( du2 ) );
    cnt = 0;
}
void add( LL x,LL y ){
    ve1[x].pb( y );
    du1[y]++;
}
queue<LL> que1;
bool topsort(){
    for( LL i = 1;i <= n;i++ ){
        if( !du1[i] ){
            que1.push( i );
            cnt++;
        }
    }
    while( que1.size() ){
        LL x = que1.front();
        que1.pop();
        for( LL i = 0;i < ve1[x].size();i++ ){
            LL y = ve1[x][i];
            if( a[y].first >= a[x].second ) return false;
            a[y].second = min( a[y].second,a[x].second-1 );
            du1[y]--;
            if( du1[y] == 0 ) {
                cnt++;
                que1.push(y);
            }
        }
    }
    return true;
}
void build(){
    for( LL x = 1;x <= n;x++ ){
        for( LL i = 0;i < ve1[x].size();i++ ){
            LL y = ve1[x][i];
            ve2[y].pb( x );
            du2[x]++;
        }
    }
}
priority_queue<piii> que2,que3;
bool topsort2(){
    LL num = 0;
    for( LL i = 1;i <= n;i++ ){
        if( !du2[i] ){
            que2.push( piii( pii(-a[i].first,-a[i].second),i ) );
        }
    }
    for( LL i = 1;i <= n;i++ ){
        while( que2.size() && -que2.top().first.first <= i ){
            piii x = que2.top();
            que3.push( piii( pii( x.first.second,x.first.first ),x.second ) );
            que2.pop();
        }
        if( !que3.size() ) return false;
        piii x = que3.top();
        que3.pop();
        if( -x.first.first < i ) return false;
        ans.pb( x.second );
        num++;
        for( LL i = 0;i < ve2[x.second].size();i++ ){
            LL y = ve2[x.second][i];
            du2[y]--;
            if( !du2[y] ){
                que2.push( piii( pii( -a[y].first,-a[y].second ),y ) );
            }
        }
    }
    if( num < n ) return false;
    return true;
}
int main()
{
    LL m,l,r,x,y;
    scanf("%I64d%I64d",&n,&m);
    init();
    for( LL i = 1;i <= n;i++ ){
        scanf("%I64d%I64d",&l,&r);
        a[i].first = l;
        a[i].second = r;
    }
    for( LL i = 1;i <= m;i++ ){
        scanf("%I64d%I64d",&x,&y);
        add( y,x );
    }
    bool flag = topsort();
    if( cnt != n || !flag ){
        printf("-1\n");
        return 0;
    }
    build();
    flag = topsort2();
    if(!flag ){
        printf("-1\n");
        return 0;
    }
    for( LL i = 0;i < ans.size();i++ ){
        printf("%I64d\n",ans[i]);
    }
    return 0;
}

B。正着想不通咱就倒着想呗。这个删除点的过程也隐含着一个拓扑序。对于这类有拓扑序而又没有任何限制的问题搜索是一个很好的解决策略。( 跟前两天lqq问我那题如出一辙)。这个用set的写法学到了,太骚了

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL maxn=  200000 + 10;
set<LL> se[maxn];
LL vis[maxn];
void init(){
    memset( vis,0,sizeof( vis ) );
}
LL cnt;
void del( LL x ){
    if( se[x].size() == 2 ){
        vis[x] = 1;
        LL y = *se[x].begin();
        LL z = *se[x].rbegin();
        se[y].erase(x);
        se[z].erase(x);
        cnt--;
        se[y].insert(z);
        se[z].insert(y);
        if( !vis[y] ) del(y);
        if( !vis[z] ) del(z);
    }
}
int main()
{
    LL n,m,x,y;
    scanf("%I64d%I64d",&n,&m);
    init();
    cnt = n;
    for( LL i = 1;i <= m;i++ ){
        scanf("%I64d%I64d",&x,&y);
        se[x].insert(y);
        se[y].insert(x);
    }
    for( LL i = 1;i <= n;i++ ){
        if( !vis[i] ){
            del(i);
        }
    }
    if( cnt == 2 ){
        printf("Yes\n");
    }else{
        printf("No\n");
    }
    return 0;
}

D。一个很骚的dp,对我有很大的启发。

首先,我的思路:dp[ i ][ j ]代表点亮前i个点,并且把其中j个点换成别的点的小代价。想到这我就蒙了,咋记录选了那些点啊?

果断不会。。。。

题解的思路:dp[ i ][ j ][ k ][ p ][ q ]. 第一维代表考虑前i个点,第2,3维分别代表第i个和第i-1个的状态。( 1代表点亮)第四维代表免费了p个点,第五维代表选了q个未点亮的点。

体会:这题每个点就两种状态,要末是点亮而又免费了的点,要末是没点亮而有了花费的点。这尼玛和背包问题有神魔区别,我最开始为什末想要记录这个点交换了后面的那些点,这些点到后来就不选了。我关心具体是哪几对点被交换了吗?不,我只关心交换点的个数以及他们的价值。这就是选与不选的问题了,背包很容易解决。

写代码的过程还是很曲折的,最开始WA了好几次,总结一下。

首先,边界条件有一些技巧,不合理的状态这里可以赋值为inf(因为这里要求最小值)。

其次,划重点,一开始不合理的状态在几次转移以后有可能变为合理的状态,要是不注意这些状态的值就完了(QAQ)。所以我把mesmet了dp数组。

 

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL maxn = 250005;
const LL maxm = 10;
const LL inf = 0x3f3f3f3f3f3f3f3f;
LL dp[maxn][2][2][maxm][maxm],a[maxn];
int main()
{
    LL n,k,m;
    scanf("%I64d%I64d",&n,&m);
    for( LL i = 1;i <= n;i++ ) scanf("%I64d",&a[i]);
    memset( dp,0x3f,sizeof( dp ) );
    dp[1][0][1][0][0] = a[1];
    dp[1][0][1][1][0] = 0;
    dp[1][0][0][0][0] = 0;
    dp[1][0][0][0][1] = a[1];
    for( LL i = 2;i <= n;i++ ){
        for( LL j = 0;j <= m;j++ ){
            for( LL k = 0;k <= m;k++ ){
                dp[i][0][0][j][k] = dp[i-1][1][0][j][k];
                if(k)dp[i][0][0][j][k] = min(dp[i][0][0][j][k],dp[i-1][1][0][j][k-1] + a[i]);
                //dp[i][0][0][j][k] = min( dp[i][0][0][j][k],dp[i-1][0][0][j][k-1] + a[i] );
                //dp[i][0][0][j][k] = min( dp[i][0][0][j][k],dp[i-1][1][0][j][k-1] + a[i] );
                dp[i][0][1][j][k] = dp[i-1][0][0][j][k] + a[i];
                if(j) dp[i][0][1][j][k] = min(dp[i][0][1][j][k],dp[i-1][0][0][j-1][k] );
                dp[i][0][1][j][k] = min( dp[i][0][1][j][k],dp[i-1][1][0][j][k] + a[i] );
                if(j)dp[i][0][1][j][k] = min( dp[i][0][1][j][k],dp[i-1][1][0][j-1][k] );
                dp[i][1][0][j][k] = dp[i-1][0][1][j][k];
                if(k)dp[i][1][0][j][k] = min(dp[i][1][0][j][k],dp[i-1][0][1][j][k-1] + a[i] );
                dp[i][1][0][j][k] = min( dp[i][1][0][j][k],dp[i-1][1][1][j][k] );
                if(k)dp[i][1][0][j][k] = min( dp[i][1][0][j][k],dp[i-1][1][1][j][k-1] + a[i] );
                dp[i][1][1][j][k] = dp[i-1][0][1][j][k] + a[i];
                if(j)dp[i][1][1][j][k] = min(dp[i][1][1][j][k],dp[i-1][0][1][j-1][k] );
                dp[i][1][1][j][k] = min( dp[i][1][1][j][k],dp[i-1][1][1][j][k] + a[i] );
                if(j)dp[i][1][1][j][k] = min( dp[i][1][1][j][k],dp[i-1][1][1][j-1][k] );
            }
        }
    }
    LL ans = inf;
    LL h = min( m,n/2 );
    for( LL i= 0; i <= h;i++ ){
        ans = min( ans,dp[n][0][1][i][i] );
        ans = min( ans,dp[n][1][0][i][i] );
        ans = min( ans,dp[n][1][1][i][i] );
    }
    printf("%I64d\n",ans);
    return 0;
}

H。首先做这道题需要知道  N / 1 + N / 2 + N / 3 +。。。+N / N = NlogN。然后加上    记忆化!!!!!(我最开始没加T掉了)

预处理出每个点以后满足序列长度要求的最长长度。(我用双指针写的,边界条件需要特判,流下了代码能力弱小的泪水)


 

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pii;
const LL maxn = 100000 + 10;
LL n,a[maxn],cnt[maxn],vis[maxn];
pii ans[maxn];
void init(){
    memset( vis,0,sizeof( vis ) );
    LL l = 1,r = 3;
    while( r <= n ){
        bool flag1 = a[r-1] >= a[r-2];
        bool flag2 = a[r] >= a[r-1];
        if( flag1 == flag2 && r != n ){
            r++;
        }else{
            if( r == n ){
                if( flag1 == flag2 ){
                    for( LL i = l;i <= r;i++ ) cnt[i] = r - i+1;
                }else{
                    for( LL i = l; i < n-1;i++ ) cnt[i] = r-i;
                }
                break;
            }
            for( LL i = l;i < r;i++ ){
                cnt[i] = r-i;
            }
            flag1 = a[r] >= a[r-1];
            flag2 = a[r+1] >= a[r];
            if( flag1 == flag2 ){
                l = r-1;
            }else{
                cnt[r-1] = 2;
                l = r;
            }
            r++;
        }
    }
    cnt[n] = 1;
    cnt[n-1] = 2;
}
int main()
{
    //freopen("123.txt","w",stdout);
    LL Q,x;
    scanf("%I64d",&n);
    for( LL i = 1;i <= n;i++ ){
        scanf("%I64d",&a[i]);
    }
    init();
    scanf("%I64d",&Q);
    for( LL i = 1;i <= Q;i++ ){
        scanf("%I64d",&x);
        LL bad = 0;
        LL cc = 0;
        if( vis[x] == 0 ){
        for( LL i = 1;i <= n;i += max(x,cnt[i]) ){
            bad += max(0LL,min( max(x,cnt[i]),n-i+1 )-cnt[i] );
            cc++;
        }
        ans[x].first = cc;
        ans[x].second = bad;
        vis[x] = 1;
        }
        printf("%I64d %I64d\n", ans[x].first ,ans[x].second);
    }
    return 0;
}

J.公式好推,需要注意的是  两数相乘上取整的写法。a / b = (a + b-1) / b .前提是两者均为正,否则需要判断取模判断。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL inf= -1;
const LL maxn= 2000 + 10;
LL x[maxn],y[maxn];
int main()
{
    LL n;
    scanf("%I64d",&n);
    scanf("%I64d",&x[0]);
    y[0] = 0;
    for( LL i = 1;i < 2*n;i++ ){
        scanf("%I64d",&x[i]);
        if( i %2 ){
            y[i] = y[i-1] + x[i] - x[i-1];
        }else{
            y[i] = y[i-1] - x[i] +x[i-1];
        }
    }
    LL a;
    scanf("%I64d",&a);
    LL p = upper_bound( x,x+2*n,a ) - x;
    p--;
    LL b;
    if( p % 2 == 0 ){
        b = (a - x[p]) + y[p];
    }else{
        b = y[p] - ( a - x[p] );
    }
    LL ans = inf;
    for( LL i = 1;i <=p && a != x[i] ;i += 2 ){
        ans = max( ans,(b*x[i] - a*y[i])/(x[i] - a) + (  ((b*x[i] - a*y[i]) * (x[i] - a) > 0 && (b*x[i] - a*y[i]) % (x[i] - a)) ? 1LL : 0LL) );
    }
    ans = max( ans,0LL );
    printf("%I64d\n",ans);
    return 0;
}

 M

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL inf= 0x3f3f3f3f3f3f3f3f;
int main()
{
    LL n,k,a,tot,x,y;
    scanf("%I64d%I64d%I64d",&n,&k,&a);
    tot = k / a;
    if( k % a ) tot++;
    LL ans = inf;
    for( LL i = 1;i<= n;i++ ){
        scanf("%I64d%I64d",&x,&y);
        if( tot % x )ans = min(ans,tot + y * (tot/x) );
        else ans = min( ans,tot+y*( tot/x-1 ) );
    }
    printf("%I64d",ans);
    return 0;
}

L

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL maxn= 250000 + 10;
int main()
{
    int k;
    char str[maxn];
    scanf("%s%d",str,&k);
    LL len = strlen( str );
    bool flag= true;
    for( LL i = 0;i < len;i++ ){
        if( str[i] != str[len-1-i] ){
            flag = false;
            break;
        }
    }
    if( flag ){
        printf("YES\n");
    }else{
        printf("NO\n");
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值