noip2017题解

这里是noip2017蒟蒻考后感……
D1T1小凯的疑惑
考场是用模拟写的,冗长的代码,拿了60分……

#include<bits/stdc++.h>
using namespace std;
const int p=100000;
long long a,b,sum,last,ans,t1,t2,head1=1,tail1=1,head2=1,tail2=1;
long long q1[100010],q2[100010];
long long read(){
    long long num=0;char c=getchar();
    for(;c<'0'||c>'9';c=getchar());
    for(;c>='0'&&c<='9';c=getchar())
        num=(num<<3)+(num<<1)+c-48;
    return num;
}

void print(long long x){
    if(x>9)print(x/10);
    putchar(x%10+48);
}

void work(){
    a=read();b=read();
    if(a==1||b==1){
        putchar('0');
        exit(0);
    }
    if(a>b)swap(a,b);
    long long k=min(b-a,a);
    sum=0;last=-1;q1[1]=q2[1]=0;
    head1=tail1=head2=tail2=1;
    while(sum<k){
        t1=q1[head1];t2=q2[head2];
        ++tail1;if(tail1>p)tail1-=p;
        ++tail2;if(tail2>p)tail2-=p;
        if(t1<t2){
            ++head1;if(head1>p)head1-=p;
            q1[tail1]=t1+a;
            q2[tail2]=t1+b;
            if(t1==last+1)
                ++sum,++last;
            else
                sum=1,last=t1;
        }else if(t1>t2){
            ++head2;if(head2>p)head2-=p;
            q1[tail1]=t2+a;
            q2[tail2]=t2+b;
            if(t2==last+1)
                ++sum,++last;
            else
                sum=1,last=t2;
        }else{
            ++head1;++head2;
            if(head1>p)head1-=p;if(head2>p)head2-=p;
            q1[tail1]=t1+a;
            q2[tail2]=t1+b;
            if(t1==last+1)
                ++sum,++last;
            else
                sum=1,last=t1;
        }
    }
    ans=last-k+1;
    while(k<a){
        ans+=a;
        k+=b-a;
    }
    print(ans-1);
}

int main(){
    work();
    return 0;
}

那么正解就是—— a×bab ……

#include<bits/stdc++.h>
using namespace std;
long long a,b,c=0;
int main(){
    scanf("%lld%lld",&a,&b);
    c=a*b-a-b;
    printf("%lld",c);
    return 0;
}

别说了吧,要落泪了。
D1T2时间复杂度
一个大模拟题,考场上少想了一点,就被卡掉一个点……
直接贴正解吧。

#include<bits/stdc++.h>
using namespace std;
struct ty{
    int f,id,in;
}st[1010];//一个栈,f存这个循环的复杂度贡献,id是为了方便存储
char temp[20];//为了解决字符读入的问题
int now,top,t,l,fuza,sum[1010],flag[30];
int read(){
    int num=0;char c=getchar();
    for(;c<'0'||c>'9';c=getchar());
    for(;c>='0'&&c<='9';c=getchar())
        num=(num<<3)+(num<<1)+c-48;
    return num;
}

void end(int x){
    for(int i=x;i<=l;++i)
        gets(temp);//ERR后直接读完
    printf("ERR\n");
}

void first(){
    memset(sum,0,sizeof(sum));
    memset(flag,0,sizeof(flag));
    l=read();
    char c=getchar();
    for(;c!='(';c=getchar());
    c=getchar();
    if(c=='n')fuza=read();
    else fuza=0;//读入给定的复杂度
    gets(temp);
}


void work(){
    st[0].f=0;st[0].id=0;top=0;
    for(int i=1;i<=l;++i){
        char c=getchar();
        if(c=='F'){
            c=getchar();
            c=getchar();
            now=c-'a';
            if(flag[now]){//如果变量名被用过
                end(i);
                return;
            }
            flag[now]=1;
            st[++top].in=now;
            st[top].id=i;
            c=getchar();c=getchar();
            if(c=='n'){
                c=getchar();c=getchar();
                if(c=='n')st[top].f=0;//n~n,0表示常数复杂度
                else st[top].f=-1;//n~1,-1表示该循环不执行
            }else{
                int x=0;
                for(;c>='0'&&c<='9';c=getchar())x=x*10+c-48;
                c=getchar();
                if(c=='n')st[top].f=1;//1~n,贡献一重的复杂度
                else{
                    int y=0;
                    for(;c>='0'&&c<='9';c=getchar())y=y*10+c-48;
                    if(x<=y)st[top].f=0;//1~1,常数
                    else st[top].f=-1;//2~1,不执行
                    continue;
                }
            }
        }else{//要结束循环
            if(!top){//如果栈空
                end(i);
                return;
            }
            flag[st[top].in]=0;
            if(st[top].f!=-1)//当前循环执行
                sum[st[top-1].id]=
                max(sum[st[top-1].id],sum[st[top].id]+st[top].f);//计算复杂度,max防止一个循环里有并列的多个循环
            --top;
        }
        gets(temp);//很重要的读入
    }
    if(top>0){//仍有循环未结束
        printf("ERR\n");//这里不用调用end()哟
        return;
    }
    if(sum[0]==fuza)printf("Yes\n");//top[0].id=0,所以最后的复杂度会在sum[0]中
    else printf("No\n");
}

void init(){
    t=read();
    for(int i=1;i<=t;++i){
        first();
        work();
    }
}

int main(){
    init();
    return 0;
}

D1T3逛公园
蒟蒻在考场放弃了这题……
正解待学习。
D2T1奶酪
这题还是很T1的,建图然后bfs搞一下,就AC了。
听说unsigned long long绝对不会爆炸,但是由于我的玄学判断,long long成功过关。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1010,MAXM=1000010;
struct line{
    int b,next;
}l[MAXM];
int t,n,sum=0,head,tail,q[MAXN],fi[MAXN],f[MAXN];
long long h,r,x[MAXN],y[MAXN],z[MAXN];
int read(){
    int num=0,flag=1;char c=getchar();
    for(;c<'0'||c>'9';c=getchar())if(c=='-')flag=-1;
    for(;c>='0'&&c<='9';c=getchar())
        num=(num<<3)+(num<<1)+c-48;
    return num*flag;
}

long long read_(){
    long long num=0,flag=1;char c=getchar();
    for(;c<'0'||c>'9';c=getchar())if(c=='-')flag=-1;
    for(;c>='0'&&c<='9';c=getchar())
        num=(num<<3)+(num<<1)+c-48;
    return num*flag;
}

void make_line(int i,int j){
    l[++sum].b=j;l[sum].next=fi[i];fi[i]=sum;
    l[++sum].b=i;l[sum].next=fi[j];fi[j]=sum;
}

bool check(int i,int j){
    long long tempx,tempy,tempz;
    tempx=abs(x[i]-x[j]);
    tempy=abs(y[i]-y[j]);
    tempz=abs(z[i]-z[j]);
    if(tempz>2*r||tempy>2*r||tempz>2*r)return false;//一点点的小优化,防止乘法爆long long
    long long temp=tempx*tempx+tempy*tempy+tempz*tempz;
    if(temp<=1LL*4*r*r)return true;
    return false;
}

void work(){
    memset(fi,0,sizeof(fi));
    n=read();h=read_();r=read_();sum=0;
    for(int i=1;i<=n;++i){
        x[i]=read_();y[i]=read_();z[i]=read_();
    }
    for(int i=1;i<=n;++i){
        if(abs(z[i])<=r)make_line(0,i);
        if(abs(h-z[i])<=r)make_line(i,n+1);//上下表面建图
        for(int j=i+1;j<=n;++j)
            if(check(i,j))make_line(i,j);//奶酪内建图
    }
    memset(f,0,sizeof(f));
    f[0]=1;
    head=1,tail=1,q[1]=0;
    while(head<=tail){//bfs
        int u=q[head];
        for(int i=fi[u];i;i=l[i].next){
            int v=l[i].b;
            if(!f[v]){
                f[v]=1;
                q[++tail]=v;
            }
            if(v==n+1){
                printf("Yes\n");
                return;
            }
        }
        ++head;
    }
    printf("No\n");
}


int main(){
    t=read();
    for(int i=1;i<=t;++i)
        work();
    return 0;
}

D2T2宝藏
这题一看数据范围就想到了去年的状压dp,一开始写错了,后来调出个80分……
我的愚蠢的代码就不放了。
正解待添加。
2017 12 17update
发现我的代码其实是可以过noip的数据的???
只是数组范围又双叒叕开小了????
我* * * *

#include<bits/stdc++.h>
using namespace std;
const int INF=40000000;
struct line{
    int y,next,w;
}l[15];
int n,m,ans=40000000,maxx=0,x,y,v,sum;
int f[13][4100],now[13][4100][13]={};//就是这里写成了12……
int d[15][15],deep[15],fi[15],val[15];
int read(){
    int num=0,flag=1;char c=getchar();
    for(;c<'0'||c>'9';c=getchar())if(c=='-')flag=-1;
    for(;c>='0'&&c<='9';c=getchar())
        num=(num<<3)+(num<<1)+c-48;
    return num*flag;
}

void dfs(int b,int s){
    if(f[b][s]<INF)return;
    for(int i=1;i<=n;++i)
        if((1<<(i-1)&s)&&i!=b){
            int t=s-(1<<(i-1));
            dfs(b,t);
            for(int j=1;j<=n;++j)
                if((1<<(j-1)&t)&&d[j][i]!=-1)
                    if(f[b][t]+d[j][i]*now[b][t][j]<f[b][s]){
                        f[b][s]=f[b][t]+d[j][i]*now[b][t][j];
                        now[b][s][i]=now[b][t][j]+1;
                        for(int k=1;k<=n;++k)
                            if(k!=i)now[b][s][k]=now[b][t][k];  
                    }
        }
    if(s==maxx)ans=min(ans,f[b][s]);
}


void work(int b){
    f[b][1<<(b-1)]=0;
    for(int i=0;i<=maxx;++i)now[b][i][b]=1;
    for(int i=1;i<=n;++i)
        if(i!=b&&d[b][i]!=-1){
            f[b][(1<<(i-1))+(1<<(b-1))]=d[b][i];
            now[b][(1<<(i-1))+(1<<(b-1))][i]=2;
        }
    dfs(b,maxx);
    return;
}

void make_line(int i,int j,int w){
    l[++sum].y=j;l[sum].next=fi[i];fi[i]=sum;l[sum].w=w;
    l[++sum].y=i;l[sum].next=fi[j];fi[j]=sum;l[sum].w=w;
}

void dfs_tree(int x,int fa){
    deep[x]=deep[fa]+1;
    for(int i=fi[x];i;i=l[i].next){
        int v=l[i].y;
        if(v!=fa){
            val[v]=l[i].w;
            dfs_tree(v,x);
        }
    }
}

void work_1(){
    for(int i=1;i<n;++i){
        x=read();y=read();v=read();
        make_line(x,y,v);
    }
    for(int i=1;i<=n;++i){
        val[i]=0;
        dfs_tree(i,0);
        int temp=0;
        for(int j=1;j<=n;++j)
            temp+=(deep[j]-1)*val[j];
        ans=min(ans,temp);
    }
    printf("%d\n",ans);
    exit(0);
}

void init(){
    memset(d,-1,sizeof(d));
    memset(f,10,sizeof(f));
    n=read();m=read();
    if(m==n-1)work_1();
    for(int i=1;i<=m;++i){
        x=read();y=read();v=read();
        if(d[x][y]!=-1)
            d[x][y]=d[y][x]=min(d[x][y],v);
        else d[x][y]=d[y][x]=v;
    }
    for(int i=0;i<n;++i)maxx+=(1<<i);
    for(int i=1;i<=n;++i)work(i);
    printf("%d\n",ans);
}

int main(){
    init();
    return 0;
}

D2T3列队
考场果断选择30分……
正解是并不会的树状数组……
那么就……待添加。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值