2013长春regional题目详解

和队友打模拟赛遗憾的打铁了,在这里总结一下题,还是太菜了。。
题目难度由易到难的顺序讲解。
C题队友wa了4发后A了,一开始题意都不太理解,但实际上就是求N个数组成的所有数中的就行了。仔细想想其实题目不是很难理解。
代码如下:

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int num[45];
ll dp[45][40005];
int N;
double P;
void solve()
{
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;
    for(int i=1; i<=N; i++)
    {
        for(int j=0;j<=40000;j++)if(dp[i-1][j])
        {
            dp[i][j+num[i]]+=dp[i-1][j];
            dp[i][j]+=dp[i-1][j];
        }
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {

        scanf("%d %lf",&N,&P);
        for(int i=1; i<=N; i++)
            scanf("%d",&num[i]);
        solve();
        long long choose=ceil(((1LL<<N)-1)*P);
        for(int i=1;i<=40000;i++)
        {
            if(choose<=0)
            {
                cout<<i-1<<endl;
                break;
            }
            choose-=dp[N][i];

        }
    }


}

I题思路:一开始博主有一个思路,上去写了后发现事情并不简单,在判断以哪个点结束的串是否可行的时候出现了问题。看了题解之后瞬间懂了。当时也写了hash,但没想到可以这样遍历串:首先枚举起点i,算这个串之后并不是枚举i+1,而是把该串的开头的L串删掉,再从末尾加上一个L串,然后在判断。
所以枚举一开始的起点就是从0到L-1即可,后面跳的过程判断是否可行用map的大小即可。
代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn=100005;
const ull hashsize=19560817;
ull p[maxn+10];
ull S[maxn+10];
int L,M;
string a;
void init()
{
    int len=a.size();
    for(int i=1;i<=len;i++)
        S[i]=S[i-1]*hashsize+a[i-1];
}
ull HS(int l,int r)
{
    if(l==r)return a[l];
    l++,r++;
    return S[r]-S[l-1]*p[r-l+1];
}
map<ull,int>themap;
void solve()
{
    int ans=0;
    int len=a.size();
    for(int i=0;i<L&&i+M*L-1<len;i++)
        {
            int now=0;
            themap.clear();
            while(now<M)
            {
                themap[HS(i+now*L,i+now*L+L-1)]++;
                now++;
            }
            if(themap.size()==M)
                ans++;
            while(i+now*L+L-1<len)
            {
                ull temp=HS(i+now*L,i+now*L+L-1);
                themap[temp]++;
                temp=HS(i+L*(now-M),i+L*(now-M)+L-1);
                themap[temp]--;
                if(themap[temp]==0)
                    themap.erase(temp);
                if(themap.size()==M)
                ans++;
                now++;
            }
        }
        cout<<ans<<endl;
}
int main()
{
    p[0]=1;
    for(int i=1;i<=maxn;i++)
        p[i]=p[i-1]*hashsize;
   while(~scanf("%d %d",&M,&L))
   {
       cin>>a;
       init();
        solve();
   }
}

G题思路:G题做不出来的就是菜啊。。。一道模板题,这题没有解题思路,就是套模板即可,2维线段树区间查询单点修改求最值。

#include<iostream>
#include<algorithm>

using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define MAXN 805   ///大小
struct seg   /// 单点修改,区间查询极值模板
{
    int xL, xR, yL, yR, val;       /// 查询时xL,xR,yL,yR为边界,修改时(xL,yL)为修改点val为值
    int maxv, minv;
    int Max[MAXN << 2][MAXN << 2], Min[MAXN << 2][MAXN << 2];
    int N, mat[MAXN][MAXN];
    void init()
    {
        maxv = -(1 << 30);
        minv = 1 << 30;
    }
    void PushUp(int xrt, int rt)
    {
        Max[xrt][rt] = max(Max[xrt][rt << 1], Max[xrt][rt << 1 | 1]);
        Min[xrt][rt] = min(Min[xrt][rt << 1], Min[xrt][rt << 1 | 1]);
    }
    void BuildY(int xrt, int x, int l, int r, int rt)
    {
        int m;
        if (l == r)
        {
            if (x != -1)Max[xrt][rt] = Min[xrt][rt] = mat[x][l];
            else
            {
                Max[xrt][rt] = max(Max[xrt << 1][rt], Max[xrt << 1 | 1][rt]);
                Min[xrt][rt] = min(Min[xrt << 1][rt], Min[xrt << 1 | 1][rt]);
            }
            return;
        }
        m = l + r >> 1;
        BuildY(xrt, x, lson);
        BuildY(xrt, x, rson);
        PushUp(xrt, rt);
    }

    void BuildX(int l, int r, int rt)
    {
        int m;
        if (l == r)
        {
            BuildY(rt, l, 1, N, 1);
            return;
        }
        m = l + r >> 1;
        BuildX(lson);
        BuildX(rson);
        BuildY(rt, -1, 1, N, 1);
    }
    void UpdateY(int xrt, int x, int l, int r, int rt)
    {
        int m;
        if (l == r)
        {
            if (x != -1)Max[xrt][rt] = Min[xrt][rt] = val;
            else
            {
                Max[xrt][rt] = max(Max[xrt << 1][rt], Max[xrt << 1 | 1][rt]);
                Min[xrt][rt] = min(Min[xrt << 1][rt], Min[xrt << 1 | 1][rt]);
            }
            return;
        }
        m = (l + r) >> 1;
        if (yL <= m)UpdateY(xrt, x, lson);
        else UpdateY(xrt, x, rson);
        PushUp(xrt, rt);
    }
    void UpdateX(int l, int r, int rt)
    {
        int m;
        if (l == r)
        {
            UpdateY(rt, l, 1, N, 1);
            return;
        }
        m = (l + r) >> 1;
        if (xL <= m)UpdateX(lson);
        else UpdateX(rson);
        UpdateY(rt, -1, 1, N, 1);
    }
    void QueryY(int xrt, int l, int r, int rt)
    {
        int m;
        if (yL <= l&&yR >= r)
        {
            minv = min(minv, Min[xrt][rt]);
            maxv = max(maxv, Max[xrt][rt]);
            return;
        }
        m = (l + r) >> 1;
        if (yL <= m)QueryY(xrt, lson);
        if (yR>m)QueryY(xrt, rson);
    }
    void QueryX(int l, int r, int rt)
    {
        int m;
        if (xL <= l&&xR >= r)
        {
            QueryY(rt, 1, N, 1);
            return;
        }
        m = (l + r) >> 1;
        if (xL <= m)QueryX(lson);
        if (xR>m)QueryX(rson);
    }
}Seg;
int main()
{
    int T;
    scanf("%d", &T);
    for (int cas = 1;cas <= T;cas++)
    {
        scanf("%d", &Seg.N);
        for (int i = 1;i <= Seg.N;i++)
            for (int j = 1;j <= Seg.N;j++)
                scanf("%d", &Seg.mat[i][j]);
        Seg.BuildX(1, Seg.N, 1);
        int Q;
        scanf("%d", &Q);
        printf("Case #%d:\n", cas);
        int L, x, y;
        while (Q--)
        {
            scanf("%d %d %d", &x, &y, &L);
            int length = (L - 1) / 2;
            Seg.xL = max(1, x - length);
            Seg.xR = min(Seg.N, x + length);
            Seg.yL = max(1, y - length);
            Seg.yR = min(Seg.N, y + length);
            Seg.init();
            Seg.QueryX(1, Seg.N, 1);
            int themax = Seg.maxv;
            int themin = Seg.minv;
            printf("%d\n", (themax + themin) / 2);
            Seg.val = (themax + themin) / 2;
            Seg.xL = x;
            Seg.yL = y;
            Seg.UpdateX(1, Seg.N, 1);
        }
    }
}

B题思路:比赛的时候知道了一个公式 θn=θn1+θn2 然后发现所有的幂都可以写成θ^2的表达方式,然后发现系数成斐波那契数列,结果越想越远,写了一个递归的方法,过后突然发现系数的问题还是不是特别好解决。
看了题解后突然发现还有一个公式: 2θn=θn+1+θn2 ,然后可以把一个数”稀释”到它的两边,不断的将系数变小,再不断的用第一个公式将连续的两个1合并到一个1就行了。

#include<iostream>
#include<stdio.h>
#include<cstring>
using namespace std;
int num[105];
int main()
{
    int N;
    while(~scanf("%d",&N))
    {
        memset(num,0,sizeof(num));
        num[50]=N;
        while(1)
        {
            bool flag=true;
            for(int i=0; i<100; i++)
            {
                if(num[i]>1)
                {
                    num[i+1]+=num[i]/2;
                    num[i-2]+=num[i]/2;
                    num[i]%=2;
                    flag=false;
                }

            }
            for(int i=0; i<100; i++)
            {
                if(num[i]>=1&&num[i+1]>=1)
                {
                    int temp=min(num[i],num[i+1]);
                    num[i]-=temp;
                    num[i+1]-=temp;
                    num[i+2]+=temp;
                    flag=false;
                }
            }
            if(flag)
                break;
        }


        int begin,end=50;
        for(int i=100; i>=50; i--)
            if(num[i]==1)
            {
                begin=i;
                break;
            }
        for(int i=0; i<=49; i++)
            if(num[i]==1)
            {
                end=i;
                break;
            }
        for(int i=begin; i>=50; i--)
        {
            if(num[i]==1)
                printf("1");
            else printf("0");
        }
        if(end!=50)
        {
            printf(".");
            for(int i=49;i>=end;i--)
            {
                if(num[i]==1)
                    printf("1");
                else printf("0");
            }
        }
        printf("\n");
    }
    return 0;
}

总结:题目都是好题,可惜不会做,做完之后感觉丰满了很多。继续努力吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值