NOIP2018游记&题解

Day 0

openday,open了一天.和yx,zyh三杀
晚上到了学军旁边的酒店.
看了一下电视睡觉了.

Day 1

早上8点到了考场,唯一的感觉是冷.
8点15分进了考场.
700+台笔记本.

密码纪念金庸
Fei2Xue@Lian#Tian!


T1 铺设道路

刚开始看题
woc这个\(T1\)我不会啊
没做过积木大赛啊QAQ
数据结构学傻掉了

于是

秒想一个分治线段树
对于一段\([L,R]\),记录一个\(val\)表示已经铺设的量,然后线段树区间最小找到\(min\)\(min\)的位置,答案加上\(val-min\),然后分治就好了.
时间复杂度\(O(n*log^2n)\)
代码略丑,见谅

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N (100010)
#define LL long long 
using namespace std;
struct xds{
    int l,r,pos;
}t[N<<3];
int n,a[N];
LL ans;
int min_(int x,int y){
    if(a[x]<a[y])return x;else return y;
}
void build(int l,int r,int x){
    t[x].l=l,t[x].r=r;
    if(l==r){
        t[x].pos=l;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,x*2),build(mid+1,r,x*2+1);
    t[x].pos=min_(t[x*2].pos,t[x*2+1].pos);
}
int query(int l,int r,int x){
    if(t[x].l==l&&t[x].r==r)return t[x].pos;
    int mid=(t[x].l+t[x].r)>>1;
    if(r<=mid)return query(l,r,x*2);
    else if(l>mid)return query(l,r,x*2+1);
    else return min_(query(l,mid,x*2),query(mid+1,r,x*2+1));
}
void work(int l,int r,int v){
    if(l>r)return;
    if(l==r){
        ans+=((LL)a[l]-(LL)v);
        return;
    }
    int mi=query(l,r,1),val=a[mi];
    ans+=((LL)val-(LL)v);
    work(l,mi-1,val),work(mi+1,r,val);
}
int main(){
    freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    build(1,n,1);
    work(1,n,0);
    printf("%lld\n",ans);
}

大概\(9:00\)左右写完了\(T1\),过了大样例,开\(T2\).

T2 货币系统

联赛前模拟赛曾出现过网友这个词语.
对于网友这个词感到莫名慌张QAQ
woc这个\(T2\)我不会做啊
仔细分析(观察大样例),发现答案必定是原来货币系统的子集.
所以直接可行性DP就好了(或者说完全背包)
一发过了大样例,和暴力拍上就不管了
时间复杂度\(O(n*a_{max})\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N (110)
#define M (25010)
#define LL long long 
using namespace std;
int n,a[N],m,ans,T;
bool vis[M];
void check(int x){
    for(int i=x;i<=m;i++)
    vis[i]|=vis[i-x];
}
int main(){
    freopen("money.in","r",stdin);
    freopen("money.out","w",stdout);
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n),m=0;
        for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),m=max(m,a[i]);
        sort(a+1,a+n+1),ans=0;
        memset(vis,0,sizeof(vis)),vis[0]=1;
        for(int i=1;i<=n;i++)
        if(!vis[a[i]])ans++,check(a[i]);
        printf("%d\n",ans);
    }
}

T3 赛道修建

看见最大值最小
果断二分
然后在树上贪心就好了
我用的是set,常数较大,得分95.
洛谷上90,开了O2跑得飞快
时间复杂度\(O(n*log^2n)\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
#define N (50010)
#define LL long long 
using namespace std;
int n,m,fi[N],ne[N<<1],b[N<<1],c[N<<1],E;
int dis[N],f[N],len[N],a[N],ru[N];
bool used[N];
multiset<int> S;
set<int>::iterator it,it2;
void add(int x,int y,int z){
    ne[++E]=fi[x],fi[x]=E,b[E]=y,c[E]=z,ru[y]++;
}
void dfs(int u,int pre){
    for(int i=fi[u];i;i=ne[i]){
        int v=b[i];
        if(v==pre)continue;
        dis[v]=dis[u]+c[i];
        dfs(v,u);
    }
}
void dp(int u,int pre,int lim){
    int cnt=0;
    for(int i=fi[u];i;i=ne[i]){
        int v=b[i];
        if(v==pre)continue;
        dp(v,u,lim),f[u]+=f[v];
    }
    for(int i=fi[u];i;i=ne[i]){
        int v=b[i];
        if(v==pre)continue;
        if(len[v]+c[i]>=lim)f[u]++;
        else a[++cnt]=len[v]+c[i];
    }
    if(!cnt)return;
    if(cnt==1){
        len[u]=a[1];
        return;
    }
    sort(a+1,a+cnt+1);
    for(int i=1;i<=cnt;i++)used[i]=0;
    //S.clear();
    for(int i=1;i<=cnt;i++)
    S.insert(a[i]);
    for(int i=1;i<=cnt;i++){
        it2=S.find(a[i]);
        if(it2==S.end())continue;
        S.erase(it2);
        it=S.lower_bound(lim-a[i]);
        if(it!=S.end()){
            f[u]++;
            S.erase(it);
        }
        else len[u]=a[i];
    }
}
bool check(int lim){
    memset(f,0,sizeof(f));
    memset(len,0,sizeof(len));
    dp(1,0,lim);
    return f[1]>=m;
}
void dp1(int u,int pre,int lim){
    int cnt=0;
    for(int i=fi[u];i;i=ne[i]){
        int v=b[i];
        if(v==pre)continue;
        dp(v,u,lim),f[u]+=f[v];
    }
    for(int i=fi[u];i;i=ne[i]){
        int v=b[i];
        if(v==pre)continue;
        if(len[v]+c[i]>=lim)f[u]++;
        else a[++cnt]=len[v]+c[i];
    }
    if(!cnt)return;
    if(cnt==1){
        len[u]=a[1];
        return;
    }
    sort(a+1,a+cnt+1);
    for(int i=1;i<=cnt;i++)used[i]=0;
    for(int i=1;i<=cnt;i++){
        if(used[i])continue;
        bool flag=0;
        for(int j=1;j<=cnt;j++){
            if(used[j]||i==j)continue;
            if(a[i]+a[j]>=lim){
                used[i]=used[j]=1,f[u]++,flag=1;
                break;
            }
        }
        if(!flag)len[u]=a[i];
    }
}
bool check1(int lim){
    memset(f,0,sizeof(f));
    memset(len,0,sizeof(len));
    dp1(1,0,lim);
    return f[1]>=m;
}
int main(){
    freopen("track.in","r",stdin);
    freopen("track.out","w",stdout);
    int L=0,R=0; bool flag=1;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z),add(y,x,z),R+=z;
    }
    for(int i=1;i<=n;i++)if(ru[i]>5)flag=0;
    if(n<=3000)flag=1;
    dfs(1,0);
    if(m==1){
        int ans=0,ll=1;
        for(int i=2;i<=n;i++)
        if(dis[i]>dis[ll])ll=i;
        dis[ll]=0,dfs(ll,0);
        for(int i=1;i<=n;i++)
        if(dis[i]>ans)ans=dis[i];
        printf("%d\n",ans);
        return 0;
    }
    else if(flag){
        int ans=0;
        while(L<=R){
            int mid=(L+R)>>1;
            if(check1(mid))L=mid+1,ans=mid;
            else R=mid-1;
        }
        printf("%d\n",ans);
    }
    else {
        int ans=0;
        while(L<=R){
            int mid=(L+R)>>1;
            if(check(mid))L=mid+1,ans=mid;
            else R=mid-1;
        }
        printf("%d\n",ans);
    }
}

Day1.5

从考场中出来,大家都\(AK\)了QAQ
我这个\(T3\)菊花图要\(1.5s\)的人感到很慌张
下午打了一会膈膜,休息了一下.
看见个个地方都在喷原题
\(NOIP=POI*N\)
\(T1\)自己抄自己
\(T2,T3=POI\)


Day 2

7:50到了学军.
还是8:15开门
今天的密码是XIAOshushenXIA
特殊字符不记得了.
纪念查大侠


T1 旅行

发现了这是一个\(dfs\)序的问题.
因此先拓扑排序
然后枚举一条边断掉
就好了?
时间复杂度\(O(n^2*logn)\)
环不大,应该速度还行.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N (5010)
#define LL long long
#define inf (0x7f7f7f7f)
using namespace std;
int n,m,fi[N],ne[N<<1],b[N<<1],E;
int c[N][N],ind,a[N],ans[N],q[N],ru[N],pp[N],ck[N],cir[N],sum;
bool vis[N];
void add(int x,int y){
    ne[++E]=fi[x],fi[x]=E,b[E]=y,ru[y]++;
}
void dfs(int u,int pre){
    int cnt=0; ans[++ind]=u;
    for(int i=fi[u];i;i=ne[i]){
        int v=b[i];
        if(v==pre)continue;
        c[u][++cnt]=v;
    }
    sort(c[u]+1,c[u]+cnt+1);
    for(int i=1;i<=cnt;i++)
    dfs(c[u][i],u);
}
void dfs1(int u,int pre){
    //cout<<u<<" "<<sum<<endl;
    cir[++sum]=u,vis[u]=1;
    for(int i=fi[u];i;i=ne[i]){
        int v=b[i];
        if(vis[v]||v==pre)continue;
        dfs1(v,u);
    }
}
void check(int u,int pre,int L,int R){
    int cnt=0; ck[++ind]=u;
    //cout<<u<<" "<<L<<" "<<R<<endl;
    for(int i=fi[u];i;i=ne[i]){
        int v=b[i];
        if(v==pre)continue;
        if(v==L&&u==R||v==R&&u==L)continue;
        c[u][++cnt]=v;
    }
    sort(c[u]+1,c[u]+cnt+1);
    for(int i=1;i<=cnt;i++)
    check(c[u][i],u,L,R);
}
bool min_(){
    for(int i=1;i<=n;i++)
    if(ck[i]<ans[i])return 1;
    else if(ck[i]>ans[i])return 0;
    return 0;
}
void topsort(){
    int h=0,t=0;
    for(int i=1;i<=n;i++)
    if(ru[i]==1)q[++t]=i,vis[i]=1;
    while(h<t){
        int u=q[++h];
        for(int i=fi[u];i;i=ne[i]){
            int v=b[i];
            if(vis[v])continue;
            ru[v]--;
            if(ru[v]<=1&&!vis[v])q[++t]=v,vis[v]=1;
        }
    }
    for(int i=1;i<=n;i++)
    if(!vis[i]){
        dfs1(i,0);
        //cout<<sum<<endl;
        break;
    }
    //for(int i=1;i<=sum;i++)
    //cout<<i<<" "<<cir[i]<<" ";
    //return;
    memset(ans,inf,sizeof(ans));
    for(int i=1;i<sum;i++){
        ind=0; //cout<<cir[i]<<" "<<cir[i+1]<<endl;
        check(1,0,cir[i],cir[i+1]);
        if(min_()){
            for(int j=1;j<=n;j++)ans[j]=ck[j];
        }
    }
    check(1,0,cir[sum],cir[1]);
    if(min_())
    for(int j=1;j<=n;j++)ans[j]=ck[j];
}
int main(){
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    if(m==n-1){
        dfs(1,0);
        for(int i=1;i<=n;i++)printf("%d ",ans[i]);
        return 0;
    }
    topsort();
    for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    return 0;
}

然后就去肝\(T2,T3\),然而我并不会其中任意一道.
因此\(rush\)了两个暴力,然后去推\(T2\)了.
推了一个\(65\)分的,结果不知道为什么挂了.
然后\(11:40\)的时候发现\(T3\)暴力挂了.
然后赶紧改了一下.
好像\(inf\)开小了,最后才\(40\)分.


T2 填数游戏

至今还是不会(逃
不贴代码了


T3 保卫王国

机房巨魔txc几天前刚学会动态dp
今天就考到了板子题.
然而并不会动态dp,也不会倍增.
于是考后学习了一个动态dp
考虑最小代价=驻军费用和-不驻军城市费用和
因此要使不驻军城市费用和最大
并且不驻军城市是树上的一个独立集
强制选或不选可以用把权值赋为\(\pm\inf\)来实现
于是剩下的就是这道题
代码如下(略长,见谅)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N (100010)
#define M (200010)
#define inf 1e11
#define rg register int
#define Label puts("NAIVE")
typedef long double ld;
typedef long long LL;
typedef unsigned long long ull;
using namespace std;
inline char read(){
    static const int IN_LEN=1000000;
    static char buf[IN_LEN],*s,*t;
    return (s==t?t=(s=buf)+fread(buf,1,IN_LEN,stdin),(s==t?-1:*s++):*s++);
}
template<class T>
inline void read(T &x){
    static bool iosig;
    static char c;
    for(iosig=false,c=read();!isdigit(c);c=read()){
        if(c=='-')iosig=true;
        if(c==-1)return;
    }
    for(x=0;isdigit(c);c=read())x=((x+(x<<2))<<1)+(c^'0');
    if(iosig)x=-x;
}
inline char readchar(){
    static char c;
    for(c=read();!isalpha(c);c=read())
    if(c==-1)return 0;
    return c;
}
const int OUT_LEN = 10000000;
char obuf[OUT_LEN],*ooh=obuf;
inline void print(char c) {
    if(ooh==obuf+OUT_LEN)fwrite(obuf,1,OUT_LEN,stdout),ooh=obuf;
    *ooh++=c;
}
template<class T>
inline void print(T x){
    static int buf[30],cnt;
    if(x==0)print('0');
    else{
        if(x<0)print('-'),x=-x;
        for(cnt=0;x;x/=10)buf[++cnt]=x%10+48;
        while(cnt)print((char)buf[cnt--]);
    }
}
inline void flush(){fwrite(obuf,1,ooh-obuf,stdout);}
#define int LL
struct Matrix{
    LL a[2][2];
    Matrix(){memset(a,0,sizeof(a));}
    Matrix operator *(Matrix x){
        Matrix res;
        for(int i=0;i<2;i++)
        for(int j=0;j<2;j++)
        for(int k=0;k<2;k++)
        res.a[i][j]=max(res.a[i][j],a[i][k]+x.a[k][j]);
        return res;
    }
}a[N<<4],val[N],ans;
int n,m,kkk;
int fi[M],ne[M],b[M],E,ind;
int top[N],fa[N],siz[N],son[N],dfn[N],rdfn[N],ed[N];
LL f[N][2],all,w[N];
void add(int x,int y){
    ne[++E]=fi[x],fi[x]=E,b[E]=y;
}
void dfs1(int u,int pre){
    int maxsiz=-1,ma=0; fa[u]=pre;
    for(int i=fi[u];i;i=ne[i]){
        int v=b[i];
        if(v==pre)continue;
        dfs1(v,u);
        if(siz[v]>maxsiz)maxsiz=siz[v],ma=v;
        siz[u]+=siz[v];
    }
    son[u]=ma,siz[u]++;
}
void dfs2(int u){
    dfn[u]=++ind,rdfn[ind]=u;
    if(!son[u]){ed[u]=u;return;}
    top[son[u]]=top[u],dfs2(son[u]),ed[u]=ed[son[u]];
    for(int i=fi[u];i;i=ne[i]){
        int v=b[i];
        if(v==son[u]||v==fa[u])continue;
        top[v]=v,dfs2(v);
    }
}
void dp(int u){
    for(int i=fi[u];i;i=ne[i]){
        int v=b[i];
        if(v==fa[u])continue;
        dp(v),f[u][0]+=max(f[v][0],f[v][1]);
        f[u][1]+=f[v][0];
    }
    f[u][1]+=1ll*w[u];
}
void build(int l,int r,int x){
    if(l==r){
        int u=rdfn[l],g0=0,g1=w[u];
        for(int i=fi[u];i;i=ne[i]){
            int v=b[i];
            if(v==fa[u]||v==son[u])continue;
            g0+=max(f[v][0],f[v][1]),g1+=f[v][0];
        }
        a[x].a[0][0]=a[x].a[0][1]=g0;
        a[x].a[1][0]=g1,a[x].a[1][1]=-inf;
        val[l]=a[x];
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,x*2),build(mid+1,r,x*2+1);
    a[x]=a[x*2]*a[x*2+1];
}
void change(int k,int l,int r,int x){
    if(l==r){a[x]=val[l];return;}
    int mid=(l+r)>>1;
    if(k<=mid)change(k,l,mid,x*2);
    else change(k,mid+1,r,x*2+1);
    a[x]=a[x*2]*a[x*2+1];
}
Matrix query(int l,int r,int L,int R,int x){
    if(l==L&&r==R)return a[x];
    int mid=(L+R)>>1;
    if(r<=mid)return query(l,r,L,mid,x*2);
    else if(l>mid)return query(l,r,mid+1,R,x*2+1);
    else return query(l,mid,L,mid,x*2)*query(mid+1,r,mid+1,R,x*2+1);
}
void update(int u,LL t){
    int pos=dfn[u];
    val[pos].a[1][0]+=t-w[u],w[u]=t;
    Matrix pre,now;
    while(u){
        pre=query(dfn[top[u]],dfn[ed[u]],1,n,1);
        change(pos,1,n,1);
        now=query(dfn[top[u]],dfn[ed[u]],1,n,1);
        u=fa[top[u]],pos=dfn[u];
        val[pos].a[0][0]+=max(now.a[0][0],now.a[1][0])-max(pre.a[0][0],pre.a[1][0]);
        val[pos].a[0][1]=val[pos].a[0][0];
        val[pos].a[1][0]+=now.a[0][0]-pre.a[0][0];
    }
}
signed main(){
    freopen("defense.in","r",stdin);
    freopen("defense.out","w",stdout);
    read(n),read(m),read(kkk);
    for(int i=1;i<=n;i++)read(w[i]),all+=1ll*w[i];
    for(int i=1,x,y;i<n;i++){
        read(x),read(y);
        add(x,y),add(y,x);
    }
    dfs1(1,0),top[1]=1,dfs2(1),fa[1]=0;
    dp(1),build(1,n,1);
    while(m--){
        int x,y,tmp1,tmp2,op1,op2;
        read(x),read(op1),read(y),read(op2);
        if(fa[x]==y||fa[y]==x){
            if((!op1)&&(!op2)){
                puts("-1");
                continue;
            }
        }
        tmp1=w[x],tmp2=w[y];
        if(op1)update(x,-inf);else update(x,inf);
        if(op2)update(y,-inf);else update(y,inf);
        ans=query(1,dfn[ed[1]],1,n,1);
        LL tmp=max(ans.a[0][0],ans.a[1][0]);
        if(!op1)(tmp-=inf)+=tmp1;
        if(!op2)(tmp-=inf)+=tmp2;
        update(x,tmp1),update(y,tmp2);
        printf("%lld\n",all-tmp);
    }
}
总结

\(100+100+95+100+50+40=485\)
连暴力都没打满
垫底了
技不如人甘拜下风
省选比别人低\(100\)分怎么打啊...
希望学科营不要像\(NOIP\)一样啊..

转载于:https://www.cnblogs.com/Romeolong/p/10044250.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值