[NOIP] 记NOIP2017

我好像没什么长进啊

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 second

using 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分

怕是要退役了

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值