[NOIP] 记NOIP2017

来源:互联网 发布:淘宝女包2016新款上市 编辑:程序博客网 时间:2024/05/18 02:16

我好像没什么长进啊

D0

和去年一样在浪

D1

T1一眼不可做,两眼傻逼题?裴蜀定理推一推就是a(b-1)-b
T2大模拟
T3先判零环的情况,然后令 fi,j 表示到第 i 个点,当前距离为 disi+j 的方案数, disi 表示起点到 i 的最短路
重构一张图,如果 disi+wi,j=disj 那么连一条 ij 的边,按照拓扑序转移就行了

表示度数数组没有清空,被卡了30分啊啊啊啊啊啊啊啊!!!

#include <cstdio>#include <iostream>#include <algorithm>#include <cstring>#include <string>#include <queue>using namespace std;typedef pair<int,int> par;const int N=100010,inf=1<<30;inline char nc(){  static char buf[100000],*p1=buf,*p2=buf;  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}inline void rea(int &x){  char c=nc(); x=0;  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());}int t,n,m,k,p,cnt,G[N];struct iedge{  int t,nx,w;}E[N<<2],iE[N<<2];int iG[N],icnt,rG[N];void iclear(){  memset(iG,0,sizeof(iG)); icnt=0;  memset(G,0,sizeof(G)); cnt=0;  memset(rG,0,sizeof(rG));}inline void addedge(int x,int y,int z){  E[++cnt].t=y; E[cnt].nx=G[x]; E[cnt].w=z; G[x]=cnt;  E[++cnt].t=x; E[cnt].nx=rG[y]; rG[y]=cnt; E[cnt].w=z;}inline void iaddedge(int x,int y){  iE[++icnt].t=y; iE[icnt].nx=iG[x]; iG[x]=icnt;}int dis[N],rdis[N],du[N];priority_queue<par> Q;inline void dij(int s,int *cG,int *cdis){  for(int i=1;i<=n;i++) cdis[i]=inf;  cdis[s]=0; Q.push(par(0,s));  while(!Q.empty()){    int x=Q.top().second,y=-Q.top().first; Q.pop();    if(y!=cdis[x]) continue;    for(int i=cG[x];i;i=E[i].nx)      if(cdis[E[i].t]>cdis[x]+E[i].w){    cdis[E[i].t]=cdis[x]+E[i].w;    Q.push(par(-cdis[E[i].t],E[i].t));      }  }}int vis[N],low[N],dfn[N],sta[N],tp,tms,imx;void tarjan(int x){  low[x]=dfn[x]=++tms;  vis[x]=1; sta[++tp]=x;  for(int i=iG[x];i;i=iE[i].nx){    if(!vis[iE[i].t]) tarjan(iE[i].t);    if(vis[iE[i].t]==1) low[x]=min(low[x],low[iE[i].t]);  }  if(low[x]==dfn[x]){    int cura=inf,curb=inf,cur,ccnt=0;    while(1){      cur=sta[tp--]; vis[cur]=2;       ccnt++;      cura=min(cura,dis[cur]); curb=min(curb,rdis[cur]);      if(cur==x || !tp) break;    }    if(ccnt>1)       imx=min(imx,cura+curb);  }}inline bool nooo(){  dij(n,rG,rdis);  for(int i=1;i<=n;i++) vis[i]=low[i]=dfn[i]=0;  tms=tp=0; imx=inf;  for(int i=1;i<=n;i++)    if(!vis[i]) tarjan(i);  return imx<=dis[n]+k;}int f[N][55];int q[N],l,r;int main(){  rea(t);  while(t--){    iclear();    rea(n); rea(m); rea(k); rea(p);    for(int i=1,x,y,z;i<=m;i++){      rea(x); rea(y); rea(z); addedge(x,y,z);      if(z==0) iaddedge(x,y);    }    dij(1,G,dis);     if(nooo()){      puts("-1"); continue;    }    for(int i=1;i<=n;i++)      for(int j=0;j<=k;j++){    if(dis[i]+j+rdis[i]>dis[n]+k) break;    f[i][j]=0;      }    for(int i=1;i<=n;i++) du[i]=0;    for(int x=1;x<=n;x++)      for(int i=G[x];i;i=E[i].nx)    if(dis[E[i].t]==dis[x]+E[i].w) du[E[i].t]++;    l=1; r=0;    q[r=1]=1;    while(l<=r){      int x=q[l++];      for(int i=G[x];i;i=E[i].nx)    if(dis[E[i].t]==dis[x]+E[i].w){      du[E[i].t]--;      if(!du[E[i].t]) q[++r]=E[i].t;    }    }    f[1][0]=1;    for(int j=0;j<=k;j++){      for(int ii=1;ii<=r;ii++){    int x=q[ii];    int cur=f[x][j];    if(!cur || dis[x]+j+rdis[x]>dis[n]+k) continue;    for(int i=G[x];i;i=E[i].nx){      int dd=dis[x]+j+E[i].w-dis[E[i].t];      if(dd<=k) f[E[i].t][dd]=(f[E[i].t][dd]+f[x][j])%p;    }      }    }    int ans=0;    for(int i=0;i<=k;i++) ans=(ans+f[n][i])%p;    printf("%d\n",ans);  }  return 0;}

D1就这样吧…

D2

T1并查集送分
T2好像写了个三进制的状压DP,复杂度是 O(4n)
按层DP,fi,S
令三进制状态 S 中,为0的位置表示这个点不在前 i 层中,为 1 表示在 1i1 层中, 2 表示在第 i 层,这样转移就好了……

数组开小-10

#include <cstdio>#include <iostream>#include <algorithm>using namespace std;int n,m;int lk[15][15],pw[15],val[15];int f[15][531445];int ttt[531445],lst[4150],ttt1[4150],sz[531445];inline int iinn(int x,int y){    return (x/pw[y-1])%3;}inline int cst(int S){    int ret=0;    for(;S;S-=(1<<(lst[S]-1))){        if(val[lst[S]]==(1<<30)) return 1<<30;        ret+=val[lst[S]];    }    return ret;}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++)        for(int j=1;j<=n;j++) lk[i][j]=1<<30;    for(int i=1,x,y,z;i<=m;i++){        scanf("%d%d%d",&x,&y,&z);        lk[x][y]=lk[y][x]=min(lk[x][y],z);    }    pw[0]=1; for(int i=1;i<=n;i++) pw[i]=3*pw[i-1];    for(int i=1;i<=n;i++)        for(int j=0;j<pw[n];j++) f[i][j]=1<<30;    for(int i=0;i<pw[n];i++)        for(int j=1;j<=n;j++)            if(!iinn(i,j)) ttt[i]|=1<<(j-1);            else sz[i]++;    for(int i=1;i<(1<<n);i++)        for(int j=1;j<=n;j++)            if((i>>(j-1))&1) ttt1[i]+=pw[j-1]*2;    lst[1]=1;    for(int i=2;i<(1<<n);i++)        lst[i]=lst[i>>1]+1;    for(int i=1;i<=n;i++) f[1][pw[i-1]*2]=0;    int ans=1<<30;    for(int i=1;i<=n;i++)        for(int S=0;S<pw[n];S++){            if(f[i][S]>=ans) continue;            if(sz[S]==n) ans=min(ans,f[i][S]);            for(int j=1;j<=n;j++)                if(!iinn(S,j)){                    val[j]=1<<30;                    for(int k=1;k<=n;k++)                        if(iinn(S,k)==2) val[j]=min(val[j],lk[k][j]);                }            int nxt=0,SS=ttt[S];            for(int j=1;j<=n;j++)                if(iinn(S,j)) nxt+=pw[j-1];            for(int ss=SS;ss;ss=(ss-1)&SS){                int cur=cst(ss);                if(cur==(1<<30)) continue;                f[i+1][nxt+ttt1[ss]]=min(f[i+1][nxt+ttt1[ss]],f[i][S]+cur*i);            }        }    printf("%d\n",ans);    return 0;}

T3我是个傻逼啊
暴力打挂,没开longlong,-30

大力平衡树模拟就好了…

#include <cstdio>#include <iostream>#include <algorithm>#include <cstdlib>#define fi first#define se secondusing namespace std;typedef long long ll;const int N=2000010;struct node{  node *ls,*rs;  int x,y,val,size,key,tag;}pool[N],*t;int n,m,q;inline char nc(){  static char buf[100000],*p1=buf,*p2=buf;  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;}inline void read(int &x){  char c=nc(); x=0;  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());}node *ro[N],*L;inline node *crt(int x,int y,int k,int v){  node *ret=t++;  ret->x=x; ret->y=y; ret->val=v; ret->key=k;  return ret;}inline void Up(node *x){  x->size=(x->ls?x->ls->size:0)+(x->rs?x->rs->size:0)+1;}inline void Push(node *x){  if(!x->tag) return ;  if(x->ls)    x->ls->tag+=x->tag,x->ls->key+=x->tag;  if(x->rs)    x->rs->tag+=x->tag,x->rs->key+=x->tag;  x->tag=0;}inline node *Merge(node *x,node *y){  if(!x || !y) return x?x:y;  Push(x); Push(y);  if(x->val>y->val){    x->rs=Merge(x->rs,y); Up(x); return x;  }  else{    y->ls=Merge(x,y->ls); Up(y); return y;  }}void PutAns(ll x){  if(x>=10) PutAns(x/10);  putchar(x%10+'0');}typedef pair<node*,node*> Dnode;Dnode Split(node *cur,int x){  if(!cur) return Dnode(NULL,NULL);  Push(cur); Dnode ret;  if(cur->key<x){    ret=Split(cur->rs,x);    cur->rs=ret.fi; ret.fi=cur;  }  else{    ret=Split(cur->ls,x);    cur->ls=ret.se; ret.se=cur;  }  Up(cur); return ret;}inline node *lst(node *cur){  while(cur->rs) cur=cur->rs;  return cur;}inline node *fst(node *cur){  while(cur->ls) cur=cur->ls;  return cur;}int main(){  t=pool;  read(n); read(m); read(q);  for(int i=1;i<=n;i++)    L=Merge(L,crt(i,m,i,rand()));  for(int i=1;i<=n;i++)    ro[i]=Merge(ro[i],crt(i,1,1,rand()));  while(q--){    int x,y;    read(x); read(y);    if(y==m){      Dnode A=Split(L,x),B=Split(A.se,x+1);      PutAns(1LL*(B.fi->x-1)*m+B.fi->y); putchar('\n');      B.fi->key=n; if(B.se) B.se->tag-=1,B.se->key-=1;      L=Merge(A.fi,Merge(B.se,B.fi));    }    else{      Dnode A=Split(ro[x],y),B=Split(A.se,y+1);      node *cur;      if(B.fi) cur=B.fi; else cur=lst(A.fi);      PutAns(1LL*(cur->x-1)*m+cur->y+y-cur->key); putchar('\n');      if((!B.se || fst(B.se)->key!=y+1) && y+1!=m)    B.se=Merge(crt(cur->x,cur->y+y-cur->key+1,y+1,rand()),B.se);      if(B.se) B.se->tag-=1,B.se->key-=1;      ro[x]=Merge(A.fi,B.se);      Dnode C=Split(L,x),D=Split(C.se,x+1);      D.fi->key=m-1; if(D.se) D.se->tag-=1,D.se->key-=1;      ro[x]=Merge(ro[x],D.fi);      if(cur->key==y)    cur->key=n,L=Merge(C.fi,Merge(D.se,cur));      else    L=Merge(C.fi,Merge(D.se,crt(cur->x,cur->y+y-cur->key,n,rand())));    }  }  return 0;}

分数跟去年一样490
D1T3+D2T2+D2T3=30+10+30=70
因为自己傻逼少了70分

怕是要退役了

原创粉丝点击