NOIP2017题解

小凯的疑惑

下面没有证明,只有结论

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<queue>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
#define ll long long
#define re register
#define file(a) freopen(a".in",,"r",stdin);freopen(a".out","w",stdout)
inline int gi(){
    int f=1,sum=0;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return f*sum;
}
int a,b;
int main(){
    a=gi();b=gi();
    printf("%lld\n",1ll*a*b-a-b);
    return 0;
}

时间复杂度

没什么好说的,大模拟即可,而且没有细节.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define re register
#define ll long long
inline int gi(){
    int f=1,sum=0;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return f*sum;
}
const int N=110;
char Time[110];int bl[N],Flag[N],top,jz[N],A[N],B[N],Goto[N],id[N],from[N],into[N];
int gettime();
char get();
char getFE();
int main(){
    int T=gi();
    while(T--){
        int L=gi();scanf("%s",Time+1);
        int t=gettime();int flag=1;top=0;
        memset(Flag,0,sizeof(Flag));
        for(int i=1;i<=L;i++){
            char ch=getFE();
            if(ch=='E'){jz[i]=0;if(!top)flag=0;else{Flag[bl[top]]=0;Goto[id[top]]=i;from[i]=id[top];top--;}continue;}
            bl[++top]=get()-'a';id[top]=i;if(Flag[bl[top]])flag=0;Flag[bl[top]]=1;
            ch=getchar();int x=0,y=0;
            while((ch<'0' || ch>'9') && ch!='n')ch=getchar();
            if(ch=='n')x=101;
            else while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
            ch=getchar();
            while((ch<'0' || ch>'9') && ch!='n')ch=getchar();
            if(ch=='n')y=101;
            else while(ch>='0' && ch<='9'){y=y*10+ch-'0';ch=getchar();}
            jz[i]=1;A[i]=x;B[i]=y;
        }
        if(top)flag=0;
        if(!flag){puts("ERR");continue;}
        int Mx=0,now=0;
        for(int i=1;i<=L;i++){
            if(jz[i]){
                if(A[i]>B[i]){i=Goto[i];continue;}
                if(B[i]<101 || A[i]==101)continue;
                now++;into[i]=1;
            }
            else
                if(into[from[i]]){into[from[i]]=0;now--;}
            Mx=max(Mx,now);
        }
        
        if(Mx==t)puts("Yes");
        else puts("No");
    }
    return 0;
}
char getFE(){
    char ch=getchar();
    while(ch!='F' && ch!='E')ch=getchar();
    return ch;
}
int gettime(){
    if(Time[3]=='1')return 0;
    int ret=0,pos=5;
    while(Time[pos]>='0' && Time[pos]<='9'){ret=ret*10+Time[pos]-'0';pos++;}
    return ret;
}
char get(){
    char ch=getchar();
    while(ch<'a' || ch>'z')ch=getchar();
    return ch;
}

逛公园

看到\(k\)的范围不大,想一下\(dp\).

\(dp_{i,j}\)表示到了\(i\)号点,与标准最短路的差为\(j\)的方案,记忆化搜索即可.运用一条路径更新的时候应该是要求反图的最短路.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define re register
#define ll long long
inline int gi(){
    int f=1,sum=0;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return f*sum;
}
typedef pair<int,int> pii;
priority_queue<pii,vector<pii>,greater<pii> >q;
#define mp make_pair
const int N=200010;
int dis[N],vis[N],dp[N][60],n,front[N],cnt,m,p,k,Vis[N][60];
struct node{int to,nxt,w;}e[N<<1];
void Add(int u,int v,int w){e[++cnt]=(node){v,front[u],w};front[u]=cnt;}
void dijkstra(){
    memset(dis,63,sizeof(dis));dis[n]=0;memset(vis,0,sizeof(vis));
    q.push(mp(0,n));
    while(!q.empty()){
        int u=q.top().second;q.pop();
        if(vis[u])continue;vis[u]=1;
        for(int i=front[u];i;i=e[i].nxt)
            if(i%2==0){
                int v=e[i].to;
                if(dis[v]>dis[u]+e[i].w){dis[v]=dis[u]+e[i].w;q.push(mp(dis[v],v));}
            }
    }
}
int flag,Time;
int dfs(int u,int K){
    if(Vis[u][K]==Time){flag=1;return 0;}
    if(~dp[u][K])return dp[u][K];
    Vis[u][K]=Time;int sum=0;
    for(int i=front[u];i;i=e[i].nxt)
        if(i&1){
            int v=e[i].to,kk=K-(dis[v]-dis[u]+e[i].w);
            if(kk<0 || kk>k)continue;
            sum=(sum+dfs(v,kk))%p;
            if(flag)return 0;
        }
    Vis[u][K]=Time-1;
    if(u==n && !K)sum=1;
    return dp[u][K]=sum;
}
#undef mp
int main(){
    int T=gi();
    while(T--){
        memset(front,0,sizeof(front));cnt=0;
        memset(dp,-1,sizeof(dp));
        n=gi();m=gi();k=gi();p=gi();
        for(int i=1;i<=m;i++){int u=gi(),v=gi(),w=gi();Add(u,v,w);Add(v,u,w);}
        dijkstra();int ans=0;flag=0;
        for(int i=0;i<=k;i++)
        {
            Time++;
            ans=(ans+dfs(1,i))%p;
        }
        if(!flag)printf("%d\n",ans);
        else puts("-1");
    }
    return 0;
}

奶酪

直接\(n^2\)枚举两个点然后判断连通性,判断每一个奶酪与上下的连通性,用并查集维护即可.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define re register
#define ll long long
inline int gi(){
    int f=1,sum=0;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return f*sum;
}
const int N=1010;
int f[N],n,h,r;
int find(int x){if(f[x]!=x)f[x]=find(f[x]);return f[x];}
struct node{double x,y,z;}c[N];
double dis(int i,int j){
    return sqrt((c[i].x-c[j].x)*(c[i].x-c[j].x)+(c[i].y-c[j].y)*(c[i].y-c[j].y)+(c[i].z-c[j].z)*(c[i].z-c[j].z));
}
int main(){
    int T=gi();
    while(T--){
        n=gi();h=gi();r=gi();
        for(int i=0;i<=n+1;i++)f[i]=i;
        for(int i=1;i<=n;i++)scanf("%lf%lf%lf",&c[i].x,&c[i].y,&c[i].z);
        for(int i=1;i<=n;i++)if(c[i].z<=r)f[find(i)]=0;
        for(int i=1;i<=n;i++)if(c[i].z>=h-r)f[find(i)]=n+1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(i!=j && find(i)!=find(j) && dis(i,j)<=2*r)
                    f[find(i)]=find(j);
        if(find(0)==find(n+1))puts("Yes");
        else puts("No");
    }
    return 0;
}

宝藏

\(n\)的只有12,可以往状压方面想.

不妨设\(f_s\)表示当前状态为\(s\)时的最小代价,那么更新就是选一个在\(s\)里面的点,然后扩展一个不在\(s\)里面的点,每一次钦定一个开始点即可.记住要用邻接矩阵,不然边数是\(2000\)而非\(n^2\).

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define re register
#define ll long long
inline int gi(){
    int f=1,sum=0;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return f*sum;
}
const int N=13;
int n,m,front[N],cnt,g[N][N];
int dp[1<<N],dep[N];
void dfs(int s){
    for(int u=0;u<n;u++)
        if(s&(1<<u))
            for(int v=0;v<n;v++)
                if(g[u][v]!=g[n][n] && !(s&(1<<v))){
                    if(dp[s^(1<<v)]>dp[s]+g[u][v]*dep[u]){
                        int tmp=dep[v];dep[v]=dep[u]+1;
                        dp[s^(1<<v)]=dp[s]+g[u][v]*dep[u];
                        dfs(s^(1<<v));
                        dep[v]=tmp;
                    }
                }
}
int main(){
    n=gi();m=gi();memset(g,63,sizeof(g));
    for(int i=1;i<=m;i++){
        int u=gi(),v=gi(),w=gi();
        u--;v--;
        g[u][v]=g[v][u]=min(g[u][v],w);
    }
    int ans=1e9+10;
    for(int i=0;i<n;i++){
        memset(dp,63,sizeof(dp));
        dp[1<<i]=0;dep[i]=1;
        dfs(1<<i);
        ans=min(ans,dp[(1<<n)-1]);
    }
    printf("%d\n",ans);
    return 0;
}

列队

考虑每一次离队变化\((x,y)\)改变的只有第\(x\)行和最后一列.那么我们对于每一行的前\(m-1\)个维护一个线段树,最后一列维护一个线段树,然后在对于上面提到的每一个维护一个\(vector\)存储进队的编号.然后线段树每一次查询然后放到\(vector\)里面就可以了.

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define re register
#define ll long long
inline int gi(){
    int f=1,sum=0;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return f*sum;
}
const int N=600010;
int n,m,Max,tot,rt[N];
vector<ll>v[N];
struct node{int ls,rs,sum;}t[N<<4];
void update(int&o,int l,int r,int pos){
    if(!o)o=++tot;t[o].sum++;
    if(l==r)return;int mid=(l+r)>>1;
    if(pos<=mid)update(t[o].ls,l,mid,pos);
    else update(t[o].rs,mid+1,r,pos);
}
int query(int o,int l,int r,int v){
    if(l==r)return l;
    int mid=(l+r)>>1,got=mid-l+1-t[t[o].ls].sum;
    if(got>=v)return query(t[o].ls,l,mid,v);
    else return query(t[o].rs,mid+1,r,v-got);
}
ll solve2(int,ll);
ll solve1(int,int);
int main(){
    n=gi();m=gi();int Q=gi();Max=max(n,m)+Q;
    while(Q--){
        int x=gi(),y=gi();
        if(y==m)printf("%lld\n",solve2(x,0));
        else printf("%lld\n",solve1(x,y));
    }
    return 0;
}
ll solve2(int x,ll y){
    int pos=query(rt[n+1],1,Max,x);update(rt[n+1],1,Max,pos);
    ll ans=pos<=n?1ll*pos*m:v[n+1][pos-n-1];
    v[n+1].push_back(y?y:ans);
    return ans;
}
ll solve1(int x,int y){
    int pos=query(rt[x],1,Max,y);update(rt[x],1,Max,pos);
    ll ans=pos<m?1ll*(x-1)*m+pos:v[x][pos-m];
    v[x].push_back(solve2(x,ans));
    return ans;
}

转载于:https://www.cnblogs.com/mle-world/p/10962759.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值