zoukankan      html  css  js  c++  java
  • 【题解】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('
    ');
          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 imes p)的蚯蚓,(u-u imes 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('
    ');
          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('
    ');
          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}
    ",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
    ",ans);
          }
          return 0;
    }
    
    
  • 相关阅读:
    【Win10开发】相对布局——RelativePanel控件
    【Win10开发】关于AutoSuggestBox
    【Win10开发】自定义标题栏
    线上服务器CPU100%排查
    Rest接口单元测试
    hibernate validator参数校验&自定义校验注解
    JsonView视图
    跨域(SpringBoot)
    Mybatis的分支选择和In循环
    CentOS6.5使用yum安装mysql
  • 原文地址:https://www.cnblogs.com/winlere/p/11658426.html
Copyright © 2011-2022 走看看