2017中国大学生程序设计竞赛 - 网络选拔赛 4/11 待补

 

 

首先.这次开局烂到家了,1200+人过了的03签到题,我队还一直在MLE中,因为怕超时,复杂度算不清不敢暴力.

最后还是过了07 05 04 之后才稳一点慢慢的改一点交一点 ...罚时爆炸的过了03..

 

 

 

 

 

hdu 6152 && 1003 Friend-Graph

 

这个题真的暴力也能过?还飞快.暴力不是 n3 吗. 哦不,好像并不是n3的.

 

这个题内存卡的比较紧,直接一个bool 类型了.

 

当时这个签到题我们队是最后出的,卡了很久,我一开始想并查集最后被推翻了,然后队友想了 最大团来做.然后就一直MLE.我最后写了个暴力,寻思直接是n3 我用两个vector 来存有边的和无边的,两种情况分别找,然后还想这记录1的个数多,还是0的个数多.来优化常数,可惜 都没用!!!因为MLE。。。

当时可能看到1200+人过有点不淡定了...没想到这么优雅的暴力呢.

 

 

PS: 主要是这个题目有个定理,保证6个点以上一定bad,所以很快就退出.

 

#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
const int maxn = 3e3;
bool w[maxn][maxn];

bool flag(int n)
{
    for(int i = 1; i <= n; ++ i)
    {
        for(int j = i + 1; j <= n; ++ j)
        {
            for(int k = j + 1; k <= n; ++ k)
            {
                if(w[i][j] == w[i][k] && w[i][k] == w[j][k] && w[i][j] == w[j][k])
                    return true;
            }
        }
    }
    return false;
}
int main()
{
    int t, n, m;
    scanf("%d", &t);
    while(t --)
    {
        scanf("%d", &n);
        for(int i = 1; i < n; ++ i)
        {
            for(int j = 1 + i; j <= n; ++ j)
            {
                scanf("%d", &m);
                if(m == 1)
                    w[j][i] = w[i][j] = false;
                else
                    w[j][i] = w[i][j] = true;
            }
        }

        if(flag(n))
            printf("Bad Team!\n");
        else
            printf("Great Team!\n");
    }
    return 0;
}

 

 

 

 

 

队友的xjb做法:

 

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
#include<cstdio>
#include<cstring>
#define N 3005
bool flag[N], a[N][N];
int ans, cnt[N], group[N], n, vis[N];

bool dfs( int u, int pos ){
    if(ans >= 3) return 1;//因为递归层数太多,所以这里加了个剪枝.... 
    int i, j;
    for( i = u+1; i <= n; i++){
        if( cnt[i]+pos <= ans ) return 0;
        if( a[u][i] )
        {
            for( j = 0; j < pos; j++ ) if( !a[i][ vis[j] ] ) break;
            if( j == pos )
            {
                vis[pos] = i;
                if( dfs( i, pos+1 ) ) return 1;
            }
        }
    }
    if( pos > ans )
    {
            for( i = 0; i < pos; i++ )
                group[i] = vis[i];
            ans = pos;
            return 1;
    }
    return 0;
}

void maxclique()
{
    ans=-1;
    for(int i=n;i>0;i--)
    {
        vis[0]=i;
        dfs(i,1);
        cnt[i]=ans;
    }
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        memset(a, 0, sizeof(a));
        scanf("%d", &n);
        for(int i = 1; i <= n-1; i++)
        {
            for(int j = 1; j <= n-i; j++)
            {
                scanf("%d", &a[i][i+j]);
                a[i+j][i] = a[i][i+j];
            }
        }

        int ans1, ans2;
        maxclique();
        ans1 = ans;
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                a[i][j] = !a[i][j];
        maxclique();
        ans2 = ans;
        if(ans1 >= 3 || ans2 >= 3) puts("Bad Team!");
        else puts("Great Team!");
    }
    return 0;
}

 

 

 

 

hdu 6153 &&1004 A Secret

 

队友过得,我还没学会,正解好像是扩展KMP?也可能是他瞎搞的

 

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e6+10;
const int mod = 1e9+7;
char str[maxn], str1[maxn], str2[maxn];
int len, len3;
int nt[maxn], nt3[maxn];
ll res[maxn], res3[maxn];

void getNext1()
{
    nt[0] = -1;
    int i = 0, j = -1;
    while (i <= len)
    {
        if (j == -1 || str[i] == str[j])
            nt[++i] = ++j;
        else
            j = nt[j];
    }
}

void getNext2()
{
    nt3[0] = -1;
    int i = 0, j = -1;
    while (i <= len3)
    {
        if (j == -1 || str2[i] == str2[j])
            nt3[++i] = ++j;
        else
            j = nt3[j];
    }
}

int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        memset(res, 0, sizeof(res));
        memset(res3, 0, sizeof(res3));
        memset(nt, 0, sizeof(nt));
        memset(nt3, 0, sizeof(nt3));
        scanf(" %s %s", str1, str2);
        int len1 = strlen(str1);
        int len2 = strlen(str2);
        for(int i = 0; i < len1/2; i++)
            swap(str1[i], str1[len1-i-1]);
        for(int i = 0; i < len2; i++)
            str[len2-i-1] = str2[i];
        str[len2] = '#';
        for(int i = len2+1; i < len1+len2+1; i++)
            str[i] = str1[i-len2-1];
        str[len1+len2+1] = 0;
        len = len1+len2+1;
        len3 = len2;
//        cout << str << endl;
        getNext1();
        for(int i = 0; i < len2/2; i++)
            swap(str2[i], str2[len2-i-1]);
        getNext2();
//        cout << str << endl;
        ll ans = 0;
//        int len = len1+len2+1;
        for(int i = len; i >= 1; i--)
        {
            res[i]++;
            res[nt[i]] += res[i];
        }
        for(int i = len3; i >= 1; i--)
        {
            res3[i]++;
            res3[nt3[i]] += res3[i];
        }
//        cout << str << ' ' << str2 << endl;
//        for(int i = 1; i <= len2; i++)
//            cout << res[i] << ' ' << res3[i] << endl;
        for(ll i = 1; i <= len2; i++)
        {
//            cout << i << ' ' << res[i] << endl;
            ans = (ans+(res[i]-res3[i])*i)%mod;
        }
        printf("%lld\n", ans);

    }

    return 0;
}


hdu 6154 &&1005 CaoHaha's staff

 

 

 

 

正解找规律. 但是我队找规律比较菜啊....

 

我是用了二分做的...

 

当然这个题目数据在大点 可能就要二分了吧。。

 

首先我看到题目的时候...好久队友给我讲明白了题意,我看求最小,立马套二分啊,诶 ?满足单调性.即走k步可以凑出的面积 k+1 步一定也可以啊.

好,那么就像怎么check,这个check其实感觉就好找到的规律差不多了,首先我要想使面积最大,我肯定优先走对角线,因为横着和斜着斜着每次增加 根2的长度.那么对于相同的步数的话,我肯定要是长宽越接近凑出的面积尽可能的大. 那么我先考虑步数为偶数的情况,对于二分到的步数先均分给四个边,因为是偶数,%4一定是2或0了,如果余2 那么我就可以使两个对边长度在增加1 ,这时的面积为 ch *ku *2. 然后我发现对于步数比他多1的奇数来说,他所能得到的面积正是在原来的四边形上在多扩展一个等腰梯形,且步数正好多1.所以我check就判断了奇数和偶数来check了,

至于为什么是一定多一步的,从最后一个样例5为7可以看出来为什么,然后多画几个结论就得到了.

 

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;
const int maxn = 1e5 + 10;
typedef long long ll;
ll n;
int check(ll x)
{
    ll res = x;
    if(x & 1)
    res --;
    ll len = res / 4;
    ll ch ,ku;
    ch = ku = len;
    if(res % 4)
    ch++;
    ll s = 0;
    if(x % 2 == 0)
    {
        s = ch * ku * 2;
    }
    else
    {
        double ss = 0.5 * (2*ch - 1); 
    //    cout<<x<<' '<<ss<<endl;
        s = ch * ku * 2 +  (ll)ss;
        //cout<<s<<endl;
    }
    if(s >= n)
    return 1;
    return 0;
}
int  main()
{
    int t;
    cin>>t;
    while(t--)
    {
        scanf("%lld",&n);
        ll l = 0,r = 2*1e9,mid,ans;
        while(l <= r)
        {
            mid = (l + r) >> 1;
            if(check(mid))
            r = mid - 1,ans = mid;
            else
            l = mid + 1;
        }    
        //check(7);
//        puts("");
//        check(6);
        printf("%lld\n",ans);
    }
    return 0;
}

 

 

 

 

 

hdu 6156 && 1007 Palindrome Function

 

 

2 -36 进制中 (L,R ) 回文数有多少个

让你求(l,r)满足条件的数有多少啊..这不很明显数位dp,算了复杂度,足够了.枚举每种进制,剩下的求法和10进制回文数有多少一样了.

 

我这里dp【i】【j】【k】【f】表示的是k进制下,串的起点是i,终点是j, 是否已经是回文串了.f 为0 表示否 1 表示 是

 

PS: 这里需要注意的就是前导0的问题,由于前导0的存在使得回文串的长度是不固定的,所以这里就设法解决前导0就好,另外需要开个中间数组记录一下前面填过的数,来判断回文

 

 

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>

using namespace std;
const int maxn = 1e5 + 10;
typedef long long ll;

ll dp[50][50][50][5];
// dp【i】【j】【k】【f】表示的是k进制下,串的起点是i,终点是j,
// 是否已经是回文串了. f 为0 表示否 1 表示 是 
int num[50],temp[50];
//temp 中间数组 ,用来判断回文 
ll dfs(int start,int cur,int state,int fp,int base)
//start 字符串起点,也用来确定第一个非前导0的位置.cur 当前处理到第几位
//state 是否是回文串.
// base 进制 
{
    if(cur<0)
        return state;
    if(!fp&&dp[start][cur][base][state]!=-1)
        return dp[start][cur][base][state];
    int fpmax = fp ? num[cur] : (base-1);
    ll ret = 0;
    for(int i = 0;i <= fpmax;i++)
    {
        temp[cur] = i;//确定该位的值 
        if(start == cur&&i == 0)// 前导0 
            ret+=dfs(start-1,cur-1,state,fp && i == fpmax,base);
        else if(state&&cur<(start+1)/2)//小于一半了,说明要开始判断是否回文了 
            ret+=dfs(start,cur-1,temp[start-cur]==i,fp&&i==fpmax,base);
        else//未构成回文串就继续处理下一位. 
            ret+=dfs(start,cur-1,state,fp&&i==fpmax,base);
    }
    if(!fp) 
        dp[start][cur][base][state]=ret;
    return ret;
}
ll solve(ll n,int k)
{
    int len=0;
    while(n)
    {
        num[len++]=n%k;
        n/=k;
    }
    num[len]=0;
    return dfs(len-1,len-1,1,1,k);
}

int  main()
{
    int _;
    cin>>_;
    ll L,R;
    int l,r;
    ll ans1,ans2,ans,ans3;
    int t=1;
    memset(dp,-1,sizeof dp);
    while(_--)
    {
        ans = 0;
        scanf("%lld %lld %d %d",&L,&R,&l,&r);
        for(int i = l;i <= r;i++)
        {
             ans1 = solve(L-1,i);
             ans2 = solve(R,i);
             ans3 = ans2 - ans1;
            ans += ans3*i + (R - L + 1 - ans3);
        }
         printf("Case #%d: %lld\n",t++,ans);
    }
    return 0;
}

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Marcus-Bao

万水千山总是情,只给五角行不行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值