【题解】NOIP2017 提高组 简要题解

【题解】NOIP2017 提高组 简要题解

小凯的疑惑(数论)

不讲

时间复杂度

大力模拟

奶酪

并查集模板题

宝藏

最优解一定存在一种构造方法是按照深度一步步生成所有的联通性。

枚举一个根,随后设\(dp(i,j)\)表示最大深度为\(i\)且当前联通的集合是\(j\)的最小答案。预处理\(dis(u,j)\)表示当\(j\)集合内的点都存在时,\(u\)到这些点的最短的最短边。

转移:
\[ dp(i,j)=\min \{dp(i-1,j),dp(i-1,s)+(i-1)\times \sum_{u\in j-s} dis(u,s)\} \]
你会问这样不能保证连边的时候深度为\(i-1\)啊,可能更小啊?

但是这样不会影响最优解。

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>

using namespace std;  typedef long long ll;
inline int qr(){
      register int ret=0,f=0;
      register char c=getchar();
      while(!isdigit(c))f|=c==45,c=getchar();
      while(isdigit(c)) ret=ret*10+c-48,c=getchar();
      return f?-ret:ret;
}

const int maxn=13;
const int inf=0x3f3f3f3f;
int e[maxn][maxn];
int dp[maxn][1<<maxn];
int dis[maxn][1<<maxn];
int n,m,rt;
int cnt=0;

int main(){
      memset(e,0x3f,sizeof e);
      memset(dp,0x3f,sizeof dp);
      n=qr(); m=qr();
      for(int t=1,t1,t2;t<=m;++t)
        t1=qr(),t2=qr(),e[t1][t2]=e[t2][t1]=min(e[t1][t2],qr());
      const int K=(1<<n)-1;
      for(int t=0;t<=K;++t)
        for(int g=1;g<=n;++g){
          if(t<<1>>g&1) continue;
          int f=inf;
          for(int k=1;k<=n;++k)
            if(t<<1>>k&1) f=min(f,e[g][k]);
          dis[g][t]=f;
        }
      int ans=inf;
      for(int rt=1;rt<=n;++rt){
        memset(dp,0x3f,sizeof dp);
        dp[1][1<<rt>>1]=0;
        for(int t=2;t<=n;++t){
          for(int i=1;i<=K;++i){
            dp[t][i]=dp[t-1][i];
            for(int l=i;l;--l&=i){
                  int c=l^i,ret=0;
                  if(dp[t-1][c]>=dp[t][i])continue;
                  bool f=0;
                  for(int g=1;g<=n;++g)
                    if(l<<1>>g&1){
                      if(dis[g][c]>=inf){ret=inf; break;}
                      else ret+=dis[g][c];
                      if(dp[t-1][c]+(t-1)*ret>=dp[t][i]) {f=1;break;}
                    }
                  if(f)continue;
                  dp[t][i]=min((ll)dp[t][i],dp[t-1][c]+(t-1ll)*ret);
            }
          }
          ans=min(ans,dp[t][K]);
        }
        ans=min(ans,dp[1][K]);
      }
      printf("%d\n",ans);
      return 0;
}

列队

不会

好像会线段树动态开点做法,没写

写了

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define mid ((l+r)>>1)
using namespace std;  typedef long long ll;
const int maxn=3e5+5,maxm=6e6+5;
int n,m,q,rt[maxn],ls[maxm],rs[maxm],seg[maxm],cnt;
ll id[maxn<<1];
vector<ll> e[maxn];
int que(const int&k,const int&l,const int&r,int&pos){
      if(!pos)pos=++cnt,seg[pos]=r-l+1;
      --seg[pos];
      if(l==r)return l;
      int g=ls[pos]?seg[ls[pos]]:mid-l+1;
      return g>=k?que(k,l,mid,ls[pos]):que(k-g,mid+1,r,rs[pos]);
}
int main(){
      ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
      cin>>n>>m>>q;
      for(int t=1;t<=n;++t) id[t]=1ll*t*m;
      for(int t=n+1,N=n+q,M=m+q,x,y,ans;q--;cout<<id[t++]<<'\n'){
        cin>>x>>y;
        if(y==m) id[t]=id[ans=que(x,1,N,rt[0])];
        else id[t]=(ans=que(y,1,M,rt[x]))<m?(x-1ll)*m+ans:e[x][ans-m], e[x].push_back(id[que(x,1,N,rt[0])]);
      }
      return 0;
}


posted @ 2019-10-12 20:14 谁是鸽王 阅读(...) 评论(...) 编辑 收藏