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

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

玩具迷题(送分)

用异或实现

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

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=1e5+5;
int n,m;
string data[maxn];
bool f[maxn];
int main(){
      n=qr(); m=qr();
      for(int t=0;t<n;++t)
        f[t]=qr(),cin>>data[t];
      int now=0;
      for(int t=1,t1,t2;t<=m;++t){
        t1=qr(); t2=qr();
        if(t1^f[now]) now=(now+t2)%n;
        else now=(now-t2+n)%n;
      }
      cout<<data[now]<<endl;
      return 0;
}

天天爱跑步(树上差分)

树上路径问题常见套路

将一个人从起点到终点的路径上从\(0\)开始编号,可以发现这个路径被分为三段:

  • 编号随着深度递减而递增。
  • LCA
  • 编号随着深度递增而递增。

分别维护两个\(d[u]+-w[u]\)的桶即可。

但是还没有保证只有路径上的点才可能看到这个跑步者,所以直接差分一下即可。

注意到此类路径影响点并且要差分的问题一定要单独处理LCA

瓶颈在\(LCA\),换成\(tarjan\)\(O(n)\)

//@winlere
#include<tr1/unordered_map>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;  using namespace std::tr1; typedef long long ll;
typedef vector<int>::iterator Vint;
typedef unordered_map<int,int>::iterator Uint;

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=3e5+1;
struct E{int k,val;};
vector<E> q1[maxn],q2[maxn];
typedef vector<E>::iterator Pint;
vector<int> e[maxn];
inline void add(const int&fr,const int&to){
      e[fr].push_back(to);
      e[to].push_back(fr);
}
int n,m,w[maxn],d[maxn],top[maxn],siz[maxn],son[maxn],r[maxn],ans[maxn],cnt[maxn];

namespace pre{
      void dfs(const int&now,const int&last){
        d[now]=d[r[now]=last]+1;
        siz[now]=1;
        for(Vint t=e[now].begin();t!=e[now].end();++t)
          if(*t^last) dfs(*t,now),son[now]=siz[*t]>siz[son[now]]?*t:son[now],siz[now]+=siz[*t];
      }
      void dfs2(const int&now,const int&last){
        top[now]=last;
        if(son[now])dfs2(son[now],last);
        for(Vint t=e[now].begin();t!=e[now].end();++t)
          if((*t^r[now])&&(*t^son[now]))
            dfs2(*t,*t);
      }
      inline int lca(int u,int v){
        for(;top[u]^top[v];u=r[top[u]])
          if(d[top[u]]<d[top[v]]) swap(u,v);
        return d[u]>d[v]?v:u;
      }
      inline void init(){dfs(1,0); dfs2(1,1);}
}

struct D{
      unordered_map<int,int> s;
      D(){s.clear();}
      inline void upd(const int&pos,const int&tag){s[pos]+=tag;}
      inline int que(const int&pos){Uint g=s.find(pos);return g==s.end()?0:g->second;}
}s1,s2;

void solve(const int&now,const int&last){
      ans[now]-=s1.que(d[now]+w[now])+s2.que(d[now]-w[now]);
      for(Vint t=e[now].begin();t!=e[now].end();++t) if(*t^last) solve(*t,now);
      for(Pint t=q1[now].begin();t!=q1[now].end();++t) s1.upd(t->k,t->val);
      for(Pint t=q2[now].begin();t!=q2[now].end();++t) s2.upd(t->k,t->val);
      ans[now]+=s1.que(d[now]+w[now])+s2.que(d[now]-w[now]);
}

int main(){
      n=qr(); m=qr();
      for(int t=1;t<n;++t) add(qr(),qr());
      for(int t=1;t<=n;++t) w[t]=qr();
      pre::init();
      for(int t=1,t1,t2;t<=m;++t){
        t1=qr(),t2=qr();
        int g=pre::lca(t1,t2);
        int dis=d[t1]+d[t2]-d[g]-d[g];
        if(d[t1]-d[g]==w[g]) ++ans[g];
        q1[t1].push_back((E){d[t1],1}); q1[g].push_back((E){d[t1],-1});
        q2[t2].push_back((E){d[t2]-dis,1}); q2[g].push_back((E){d[t2]-dis,-1});
      }
      solve(1,0);
      for(int t=1;t<=n;++t) printf("%d ",ans[t]);
      putchar('\n');
      return 0;
}

换教室(期望DP)

换教室

注意这是期望,所以要计算失败的贡献。

//@winlere
#include<bits/stdc++.h>
#define int long long 
using namespace std;  typedef long long ll;
template < class ccf > inline ccf qr(ccf ret){      ret=0;
      register char c=getchar();
      while(not isdigit(c)) c=getchar();
      while(isdigit(c)) ret=ret*10+c-48,c=getchar();
      return ret;
}inline int qr(){return qr(1);}
const int maxn=25;
const ll mod=1e9+7;
inline ll Pow(ll base,ll p){
      base%=mod;
      register ll ret=1;
      for(;p;p>>=1,base=base*base%mod)
        if(p&1) ret=ret*base%mod;
      return ret;
}
ll data[maxn],s,ans,inv[maxn]={1},jie[maxn]={1};
int n;

inline ll C(const ll&n,const ll&m){
      if(n<m||m<0||n<0)return 0;
      if(n==m)return 1;
      register ll ret=inv[m];
      for(register ll t=n;t>=n-m+1ll;--t)
        ret=t%mod*ret%mod;
      return ret;
}
#undef int
int main(){
#define int long long 
#ifndef ONLINE_JUDGE
      freopen("in.in","r",stdin);
      //freopen("out.out","w",stdout);
#endif
      for(register int t=1;t<maxn;++t)
        inv[t]=inv[t-1]*Pow(t,mod-2ll)%mod;
      n=qr();s=qr(1ll);ans=C(s+n-1ll,n-1ll);
      for(register int t=1;t<=n;++t)
        data[t]=qr(1ll);
      for(register int t=1,edd=1<<n,cnt=0;t<edd;++t){
        ll f=cnt=0,delt;
        for(register int i=1;i<=n;++i)
          if(t<<1>>i&1)
            f+=data[i]+1ll,++cnt;
        delt=C(s-f+n-1ll,n-1ll);
        if(cnt&1) ans=(ans-delt)%mod,ans=ans<0?ans+mod:ans;
        else ans=(ans+delt)%mod;
      }
      cout<<ans<<endl;
      return 0;
}

组合数问题(二维前缀和)

\(O(n^2)\)处理组合数然后前缀和即可。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define RP(t,a,b)   for(int t=(a),edd=(b);t<=edd;t++)
typedef long long ll;
inline ll qr(void){
    char c=getchar();
    int x=0,q=1;
    while(c<48||c>57)
        q=c==45?-1:q,c=getchar();
    while(c>=48&&c<=57)
        x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*q;
}
const int maxn=2002;
ll T,k;
int l,r;
int c[maxn][maxn]={1},ans[maxn][maxn];
int main(){
    T=qr();k=qr();
    c[1][0]++;
    c[1][1]++;
    RP(t,2,2000){
        c[t][0]=1;
        RP(i,1,t){
            c[t][i]=(c[t-1][i-1]+c[t-1][i])%k;
            ans[t][i]=ans[t-1][i]+ans[t][i-1]-ans[t-1][i-1];
            if(!c[t][i]) ans[t][i]++;
        }
        ans[t][t+1]=ans[t][t];
    }
    while(T--){
        l=qr();
        r=qr();
        if(r>l) r=l;
        cout<<ans[l][r]<<endl;
    }
    return 0;
}

蚯蚓(队列)

堆只能获得75分(不知道fib和配对堆能不能更高)。受到一堆题的启发,考虑一下用队列维护这个堆。

处理每秒的增量直接记录一个变量即可,注意到当元素push进去时要先减去当前总增量,下次取出时才能计算出真正的增量。

可以发现,后面生成的蚯蚓一定比先前生成的蚯蚓要短。

但是生成的两蚯蚓之间没有大小的保证。

所以维护三个队列分别表示:原蚯蚓,\(u \times p\)的蚯蚓,\(u-u\times p\)的蚯蚓。每次从三个队列队首取大的那个。

复杂度\(O(n)\) 居然可以过CCF老年机?

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<vector>
#define int long long

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

const int maxn=1e5+1;
queue<int> q[4];
int data[maxn];
int n,m,Q,u,v,T;
double f;

inline int getBig(){
      int g=-1e18,ret=-1;
      for(int t=1;t<=3;++t)
        if(q[t].size()&&q[t].front()>g)
          g=q[t].front(),ret=t;
      return ret;
}

signed main(){
      n=qr(); m=qr(); Q=qr(); u=qr(); v=qr(); T=qr();
      f=(double)u/v;
      for(int t=1;t<=n;++t) data[t]=qr();
      sort(data+1,data+n+1);
      for(int t=n;t;--t) q[1].push(data[t]);
      for(int t=0,g=T;t<m;++t){
        int l=getBig();
        int now=q[l].front()+t*Q;
        int f1=now*f,f2=now-f1;
        q[l].pop(); q[2].push(f1-(t+1)*Q); q[3].push(f2-(t+1)*Q);
        if(--g==0) g=T,printf("%lld ",now);
      }
      putchar('\n');
      for(int t=1,g=T;t<=m+n;++t){
        int l=getBig();
        int now=q[l].front()+m*Q;
        if(--g==0) g=T,printf("%lld ",now);
        q[l].pop();
      }
      putchar('\n');
      return 0;
}

愤怒的小鸟(搜索)

剪枝然后随机化,注意到枚举一次抛物线后,可能打中很多鸟,那些鸟就不必再和当前鸟枚举抛物线了。

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define x first
#define y second
#define pf(x) ((x)*(x))

using namespace std;  typedef long long ll;
inline int qr(){
      register int ret=0,f=0;
      register char c=getchar();
      while(c<48||c>57)f|=c==45,c=getchar();
      while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
      return f?-ret:ret;
}
const int maxn=21;
int n,W1;
typedef  pair<long double,long double> Pt;
const long double eps=1e-10;
Pt data[maxn];
bool hit[maxn];int ans;

struct axx{
      long double a,b;
      axx(){a=b=0;}
      axx(const long double&x,const long double&y){a=x,b=y;}
      inline bool operator*(const Pt&s){return abs((a*s.first+b)*s.first-s.second)<=eps;}
      inline bool operator ==(const axx&s){return abs(a-s.a)<=eps&&abs(b-s.b)<=eps;}
      inline void print(){
        printf("{%Lfx*x+%Lfx}\n",a,b);      
      }
}Err(1,1);

inline axx gen(const Pt&a,const Pt&b){
      long double mu=a.x*pf(b.x)-pf(a.x)*b.x;
      if(-eps<=mu&&mu<=eps) return Err;
      long double z1=b.y*a.x-a.y*b.x;
      long double z2=b.y*pf(a.x)-a.y*pf(b.x);
      axx ret(z1/mu,-z2/mu);
      if(ret.a>=-eps) return Err;
      return ret;
}

void dfs(const int&now,const int&s,const int&tol){
      if(ans<=s) return;
      if(tol==n) return ans=s,void();
      if(now>n) return;
      if(hit[now]) return dfs(now+1,s,tol);
      bool f=0,temp[maxn];
      for(int t=1;t<=n;++t) temp[t]=hit[t];
      unsigned short sav[maxn];
      sav[0]=0;
      for(int t=now+1;t<=n;++t)
        if(!temp[t]){
          axx Cur=gen(data[now],data[t]);
          if(Cur==Err) continue;
          for(int i=now;i<=n;++i)
            if((!hit[i])&&(Cur*data[i]))
                  sav[++*sav]=i,hit[i]=1,temp[i]=1;
          dfs(now+1,s+1,tol+*sav);
          for(int i=1;i<=*sav;++i) hit[sav[i]]=0;
          sav[0]=0;
          f=1;
        }
      if(!f) hit[now]=1,dfs(now+1,s+1,tol+1),hit[now]=0;
}

int main(){
      srand(time(NULL));
      int T=qr();
      while(T--){
        n=qr(); W1=qr();
        for(int t=1;t<=n;++t) scanf("%Lf%Lf",&data[t].x,&data[t].y);
        stable_sort(data+1,data+n+1);
        n=unique(data+1,data+n+1)-data-1;
        random_shuffle(data+1,data+n+1);
        ans=n;
        dfs(1,0,0);
        printf("%d\n",ans);
      }
      return 0;
}
posted @ 2019-10-12 09:03 谁是鸽王 阅读(...) 评论(...) 编辑 收藏