NOIP2017提高组解题报告

Day 1

T1:博主只拿了60pts exgcd,弱啊
正解其实就是a*b-a-b
这种题考场上一看数据范围应该打表测试一下,发现大概的规律再去证明
结论:对于正整数a,b满足gcd(a,b)=1,我们有ax+by=n无非负整数解的最大正整数n为ab-a-b
证明如下:
我们可以用反证法,假设存在 ax+by=abab

a(x+1)+b(y+1)=ab

a|(y+1)b|(x+1)

令ak=y+1,bj=x+1
abj+abk=ab

ab(j+k)=ab

我们知道x,y>=0,那么那么有 x+1>=1,y+1>=1
那么k>=1,j>=1,k+j>=2那个柿子不成立
所以假设错误,得证

#include <cstdio>
using namespace std;
int main()
{
    int a,b;
    scanf("%d%d",&a,&b);
    printf("%lld",(long long)a*b-a-b);
}

T2:这种大模拟没有写对我也是挺服气自己了。。
思路没理清啊,栈空时才要更新最大值,我在里面就给更新最大值了,ta当然不对啊
son[i]表示i这一层包含的最大值
own[i]表示i这一层自己的值,要是根本进不去的话设成一个最大的负数吧
这样当栈空的时候取一个最大值看看等不等于一开始的

#include <cstdio>
#include <cstring>
#include <iostream>
#define INF 1e9
using namespace std;
int ha[1005],stack[1200],num,own[1200],son[1200];
char st[1200];
int js(char st[])
{
    int i=0,b=0;
    while (st[i]>='0' && st[i]<='9') b=b*10+st[i]-'0',i++;
    return b;
}
int main()
{
    freopen("complexity.in","r",stdin);
    freopen("complexity.out","w",stdout);
    int T;
    scanf("%d",&T);
    while (T--)
    {
        int bb=0,b=0,n,i,vv=0;
        scanf("%d",&n);

        memset(ha,0,sizeof(ha));
        memset(son,0,sizeof(son));
        memset(own,0,sizeof(own));
        num=0;
        scanf("%s",st);
        int ls=strlen(st);
        if (n%2!=0) vv=1;
        for (i=0;i<ls;i++)
          if (st[i]=='(')
          {
            i++;
            if (st[i]=='1') break;
            else 
            {
                while (st[i]!='^') i++;
                i++;
                while (st[i]>='0' && st[i]<='9') b=b*10+st[i]-'0',i++;
                break;
            }
          }
        for (i=1;i<=n;i++)
        {
            char ss[205],zm[205],f[205],e[205];
            scanf("%s",ss);
            if (ss[0]=='F')
            {
                scanf("%s%s%s",&zm,&f,&e);
                if (ha[zm[0]])vv=1;
                ha[zm[0]]=1;
                if ((f[0]=='n' && e[0]!='n') || (f[0]!='n' && e[0]!='n' && js(f)>js(e))) own[++num]=-INF;
                else if ((e[0]>='0' && e[0]<='9') || (f[0]=='n' && e[0]=='n')) own[++num]=0;
                else own[++num]=1;
                son[num-1]=max(own[num],son[num-1]);
                stack[num]=zm[0];
            }
            else
            {
                if (!num)vv=1;
                ha[stack[num]]=0;
                own[num]+=son[num];
                son[num-1]=max(own[num],son[num-1]);
                son[num]=0; own[num]=0;
                num--;
                if (!num) bb=max(son[num],bb);
            }     
        }  
        if (vv || num) {printf("ERR\n");continue;}
        if (bb==b) printf("Yes\n");
        else printf("No\n"); 
    }
    return 0;
}

T3:Day1的dp。。
30pts暴力

Day 2

T1:各种数据都能过,被NOIP的数据卡了,竟然是因为精度开的太大了。。。1e-18就A了,好气哦
我用的是dij最短路判断 O(n2) 连边求就好了

#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#define LL long long 
using namespace std;
const LL INF=1e18;
const double eps=1e-18;
const int N=1005;
const int M=N*N;
struct hh{int x,y,z;}ball[N];
struct www{int id;LL z;}q[M+N];
int tot,nxt[M],point[N],v[M],num;LL dis[N];
int n,r,h,t;bool vis[N];
bool operator <(const www &a,const www &b){return a.z<b.z;}
void addline(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void push(www now)
{
    q[++num]=now;
    int x=num,fa=x>>1;
    while (fa && q[x]<q[fa])
    {
        swap(q[x],q[fa]);
        x=fa; fa>>=1;
    }
}
void pop()
{
    q[1]=q[num--];
    int now=1,l=now<<1,r=now<<1|1,better=l;
    if (r<=num && q[r]<q[l]) better=r;
    while (better<=num && q[better]<q[now])
    {
        swap(q[better],q[now]);
        now=better,l=now<<1,r=now<<1|1,better=l;
        if (r<=num && q[r]<q[l]) better=r;
    }
}
void dij()
{
    memset(dis,0x7f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[0]=0;
    push((www){0,0});
    while (num)
    {
        www now=q[1]; pop(); 
        if (vis[now.id]) continue;
        vis[now.id]=1;
        for (int i=point[now.id];i;i=nxt[i])
          if (dis[v[i]]>dis[now.id]+1)
          {
            dis[v[i]]=dis[now.id]+1;
            push((www){v[i],dis[v[i]]});
          }
    }
}
bool jiao(int i,int j)
{
    double dd=(double)sqrt((LL)(ball[i].x-ball[j].x)*(ball[i].x-ball[j].x)+(LL)(ball[i].y-ball[j].y)*(ball[i].y-ball[j].y)+(LL)(ball[i].z-ball[j].z)*(ball[i].z-ball[j].z));
    if (dd<=(double)2*r || abs(dd-2*r)<=eps) return 1;
    return 0;
}
int main()
{
    freopen("cheese.in","r",stdin);
    freopen("cheese.out","w",stdout);
    int T,i,j,nn;scanf("%d",&T);
    while (T--)
    {
        tot=0; memset(point,0,sizeof(point));
        scanf("%d%d%d",&nn,&h,&r);
        t=nn+1;n=0;
        for (i=1;i<=nn;i++) 
        {
            ++n;
            scanf("%d%d%d",&ball[n].x,&ball[n].y,&ball[n].z);
            if ((ball[n].z-r>=h) || (ball[n].z+r<=0)) n--;
        }
        for (i=1;i<=n;i++)
        {
            if (ball[i].z+r>=h) addline(i,t);
            for (j=i+1;j<=n;j++)
              if (jiao(i,j)) addline(i,j);
            if (ball[i].z-r<=0) addline(0,i);
        }
        dij();
        if (dis[t]>INF) printf("No\n");else printf("Yes\n"); 
    }
}

T2:最弱的博主今年唯一A掉的题目
其实就是一个暴力啊,分了两部分来写来稳住40pts
我们可以用dp[i]表示i这个状态(二进制),其中1表示这个点经过了,0表示没有经过
再用f[i][j]表示i这个状态每个点到达j这个点的距离,然后暴力转移一波就好了
效率是 O(n42n) 看着很大对吧,可是这是极限效率,这个效率是今年跑的最快的做法辣

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#define LL long long
using namespace std;
const int N=2200;
const LL INF=1e18;
int tot,nxt[N],point[N],v[N],c[N],f[5000][15],h[N];
LL dp[5000];bool vis[N];
void addline(int x,int y,int z)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
void spfa(int x)
{
    queue<int>q;
    q.push(x);
    while (!q.empty())
    {
        int now=q.front(); q.pop();
        vis[now]=0;
        for (int i=point[now];i;i=nxt[i])
          if (h[v[i]]>h[now]+1)
          {
            h[v[i]]=h[now]+1;
            if (!vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
          }
    }
}
int main()
{
    int n,m,i,j,k,ii,num,lz=0,l;LL ans=INF;bool vv=1;
    scanf("%d%d",&n,&m);
    for (i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        addline(x,y,z);if (lz!=z && i!=1) vv=0;
        lz=z;
    }
    if (vv)
    {
        for (i=1;i<=n;i++)
        {
            memset(h,0x7f,sizeof(h));
            memset(vis,0,sizeof(vis));h[i]=0;
            spfa(i);LL qz=0;
            for (j=1;j<=n;j++) qz+=h[j]*lz;
            ans=min(ans,qz);
        }
    }
    else
    {
    num=(1<<n)-1;
    for (i=1;i<=n;i++)
    {
        memset(dp,0x7f,sizeof(dp));
        dp[1<<i-1]=0;f[1<<i-1][i]=0;
        for (j=0;j<=num;j++)
          if (dp[j]<INF) 
          {
            for (k=1;k<=n;k++)
              if ((j>>k-1)&1) 
              {
                for (ii=point[k];ii;ii=nxt[ii])
                  if (!((j>>(v[ii]-1)&1)))
                  {
                    if (dp[j|(1<<(v[ii]-1))]>dp[j]+(LL)(f[j][k]+1)*c[ii])
                    {
                        dp[j|(1<<(v[ii]-1))]=dp[j]+(LL)(f[j][k]+1)*c[ii];
                        for (l=1;l<=n;l++) f[j|(1<<(v[ii]-1))][l]=f[j][l];
                        f[j|(1<<(v[ii]-1))][v[ii]]=f[j][k]+1;
                    }
                  } 
              }
          }
        ans=min(ans,dp[num]);
    }
    }
    printf("%lld",ans);
}

T3:waiting

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值