zzuli 第八届校赛 题解

准备了一个月的校赛终于没白费工夫,拿到了冠军...但是只过了9题...因为我字符串学的太差C题没过...

这套题说起来没有去年的好,虽然我去年打的不好吧。原题三四个...

还有一道比较难的数论没放出来....说起来花了五分钟就推出来公式了....导致最后两个小时都没题目做...

A.模拟

注意坑点有过程中某个值爆了int然后在凑出来一个0-255的数字,还有就是连续两个点..

B.DP

lightoj的原题,只是多了n==1或者m==1的情况,特判一下就好了...lightoj无1题解连接:点击查看

C. 字符串hash

我觉得可以二分答案用字符串hash判断,鸟神提出了一个思路是manacher+lcp...弱准备学好字符串在回来搞这个题。

D.预处理+查询区间最小值

可以先预处理出每个点被覆盖多少次,然后针对于每条线段进行枚举,查询这条线段的区间中最小值是多少,如果小于2说明只被这一条线段覆盖他被删除后肯定是没有其他线段覆盖..线段树没加优化可能会被卡..因为没有修改只有查询,所以我采用的ST算法,预处理为nlogn查询复杂度为1...针对这个题优势比较大。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int ja[120000],j[120000],l[120000],r[120000],x[120000];
int dp[120000][20],mm[120000];
int ans[120000];
void initrmq(int n,int b[])
{
    mm[0]=-1;
    for(int i=1;i<=n;i++)
    {
        mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
        dp[i][0]=b[i];
    }
    for(int j=1;j<=mm[n];j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
        dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
int rmq(int x,int y)
{
    int k=mm[y-x+1];
    return min(dp[x][k],dp[y-(1<<k)+1][k]);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(ja,0,sizeof(ja));
        memset(j,0,sizeof(j));
        memset(x,0,sizeof(x));
        memset(dp,0,sizeof(dp));
        int n,m;
        scanf("%d %d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d",&l[i],&r[i]);
            ja[l[i]]++;
            j[r[i]]++;
        }
        int tmp=0;
        for(int i=1;i<=n;i++)
        {
            tmp+=ja[i];
            x[i]+=tmp;
            tmp-=j[i];
        }
        initrmq(n,x);
        int cnt=0;
        for(int i=1;i<=m;i++)
        {
            int tmp=rmq(l[i],r[i]);
            if(tmp<=1)
                continue;
            else
                ans[cnt++]=i;
        }
        printf("%d\n",cnt);
        for(int i=0;i<cnt;i++)
        {
            if(i==cnt-1)
                printf("%d\n",ans[i]);
            else
                printf("%d ",ans[i]);
        }
    }
    return 0;
}
E.模拟

我直接采用了0年1月1日到两个输入的日期分别有多少天然后计算差值。这样的话关于闰年比较好计算,可以用容斥...闰年数量为x/4-x/100+x/400...

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int pd(int y)
{
    if(y%400==0) return 1;
    if(y%4==0&&y%100!=0) return 1;
    return 0;
}
int mf[13]= {0,31,28,31,30,31,30,31,31,30,31,30,31};
int mr[13]= {0,31,29,31,30,31,30,31,31,30,31,30,31};
ll js(int y,int m,int d)
{
    ll tmp=(y-1)/4-(y-1)/100+(y-1)/400;
    tmp+=(y-1)*365;
    for(int i=1;i<m;i++)
    {
        if(i==2&&pd(y))
            tmp++;
        tmp+=mf[i];
    }
    tmp+=d;
    return tmp;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int y1,m1,d1,y2,m2,d2;
        scanf("%d %d %d %d %d %d",&y1,&m1,&d1,&y2,&m2,&d2);
        ll ans=js(y1,m1,d1);
        ll tmp=js(y2,m2,d2);
        ans=tmp-ans;
        printf("%lld\n",ans);
    }
    return 0;
}

F.数论

首先我们要明确一点,gcd是在减法的基础上优化的...那么这个减法的过程真的很像最朴素的求gcd的过程啊..测试一发样例过了 提交试试,因为感觉直接模拟肯定爆炸。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define PI acos(-1.0)
int a[1200];
int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        int ans=a[1];
        int sum=a[1];
        for(int i=2;i<=n;i++)
        {
            ans=gcd(ans,a[i]);
            sum+=a[i];
        }
        printf("%d\n",sum/ans);
    }
    return 0;
}
G.折半枚举

首先这个题一眼看上去就是一个背包,然后背包的体积与价值过大..所以请参考挑战程序设计162页的超大背包求解的折半枚举方法。先把n个物品分成两堆,然后枚举出来每堆的所有状态,最大为2^15,然后将两堆都按照饱食度排序。刚开始我想的是第二堆按照饱食度为关键字建立线段树,但是价格是无序的查找太烦..然后考虑到如果用其他的的话要支持添加操作以及查询最小值和指定值,毫无疑问set最合适...

对于第一堆按照饱食度从小到大枚举,对于这个状态来言把所有饱食度与其和大于k的价格全部加入set,因为这样的话枚举后面饱食度大的情况第二堆加入的这些是肯定成立的,set里面最多加入2^15个价格。

然后查找的时候我们要查找出当前最小价格,然后看第一堆价格加最小价格是否大于x,大于x的话直接-y与当前最小值比较,否则的话我们还要选择一次第一堆价格+某个价格的值大于x且最小的...然后减去y与答案比较。

学弟用了DFS优化暴力刚好卡着时间过了...

#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct node
{
    ll p,b;
} s1[80000],s2[80000];
ll p[50],b[50];
set<ll> s;
set<ll>::iterator it;
int cmp(node a,node b)
{
    return a.b<b.b;
}
int main()
{
    int t;
    ll n;
    ll k,x,y;
    scanf("%d",&t);
    while(t--)
    {
        cin>>n>>k>>x>>y;
        ll sum=0;
        for(int i=0; i<n; i++)
        {
            cin>>p[i]>>b[i];
            sum+=b[i];
        }
        if(sum<k)
        {
            puts("go die");
            continue;
        }
        s.clear();
        int cnt1=0;
        int tn=n/2;
        for(int i=0; i<(1<<tn); i++)
        {
            ll tmpp=0,tmpb=0;
            for(int j=0; j<tn; j++)
            {
                if(i&(1<<j))
                {
                    tmpp+=p[j];
                    tmpb+=b[j];
                }
            }
            s1[cnt1].p=tmpp;
            s1[cnt1].b=tmpb;
            cnt1++;
        }
        int nt=n-tn;
        int cnt2=0;
        for(int i=0; i<(1<<nt); i++)
        {
            ll tmpp=0,tmpb=0;
            for(int j=0; j<nt; j++)
            {
                if(i&(1<<j))
                {
                    tmpp+=p[j+tn];
                    tmpb+=b[j+tn];
                }
            }
            s2[cnt2].p=tmpp;
            s2[cnt2].b=tmpb;
            cnt2++;
        }
        sort(s1,s1+cnt1,cmp);
        sort(s2,s2+cnt2,cmp);
        int j=cnt2-1;
        ll ans=10000000000LL;
        ll ax;
        for(int i=0; i<cnt1; i++)
        {
            ll tmp=max(0LL,k-s1[i].b);
            while(s2[j].b>=tmp&&j>=0)
            {
                s.insert(s2[j].p);
                j--;
            }
            if(s.empty())
                continue;
            ax=s1[i].p;
            if(ax+(*s.begin())<x)
            {
                ans=min(ans,ax+(*s.begin()));
                ll c=max(0LL,x-ax);
                it=s.lower_bound(c);
                if(it==s.end())
                    continue;
                ax+=(*it);
                ax-=y;
                ans=min(ans,ax);
            }
            else
                ans=min(ans,ax+(*s.begin())-y);
        }
        cout<<ans<<endl;
    }
    return 0;
}

H.简单并查集

直接判断有多少个根,然后答案是根数-1..

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define PI acos(-1.0)
int f[2000];
void db(int n)
{
    for(int i=1;i<=n;i++)
        f[i]=i;
}
int finde(int x)
{
    if(f[x]!=x)
        return f[x]=finde(f[x]);
    return x;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d %d",&n,&m);
        db(n);
        int u,v;
        for(int i=0;i<m;i++)
        {
            scanf("%d %d",&u,&v);
            int x=finde(u);
            int y=finde(v);
            if(x!=y)
                f[x]=y;
        }
        int ans=0;
        for(int i=1;i<=n;i++)
        {
            if(f[i]==i)
                ans++;
        }
        printf("%d\n",ans-1);
    }
    return 0;
}

I 公式题

论身边带学霸的重要性

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define PI acos(-1.0)
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,a,l;
        scanf("%d %d %d",&n,&a,&l);
        double ans=a*a*n/4.0/tan(PI/n);
        int cnt=0;
        while(ans>l)
        {
            ans=ans*(cos(PI/n)*cos(PI/n));
            cnt++;
        }
        printf("%d\n",cnt);
    }
    return 0;
}

J 签到

CF原题,如果有1肯定能凑出来所有数字,如果没1的话连1都凑不出来肯定是不行的..



你可能感兴趣的:(题解,zzuli校赛)