NOIP2017提高组总结

考前一周

wls找到我们,告诉我们一个悲痛的消息:今年巴蜀有三十多个高二企图霸场
本来没心没肺准备NOIP的蒟蒻瞬间就有压力了。。。。

Day0

期中考试考差了被老爸半夜拉出来骂了一顿,爽翻,没睡觉(12:00以后睡的)

Day1

毕竟还只是第二次参加NOIP(种种问题导致我们连前几年的NOIP题都没做),并不清楚NOIP的画风,还天真地以为今年第一天又是1水+2神的配置。于是企图先秒掉第一题,然而。。。
什么鬼玩意。。试图用exgcd求ax+by=k且x,y为自然数的情况下,k不能达到的最大值
首先得到一个ax+by=1的x,y的某两组取值(一组x为正数,一组y为正数),再以此为基础求x,y保持为非负数的周期。。乱七八糟的。。
又尝试换一个思路,求(k-ax)=y(mod b)中不满足的k的最大值。。
各种恶心的方式搞了二十多分钟,发现这么做下去第一题就很难了。
于是在8:55左右,开始进入第二题

在看了题目三四遍之后,确认我没有看错题意,也没有看错数据范围,我开始怀疑出题人是不是把第一题和第二题弄反了。
总之,良(e)好(xin)的代码风格使我很轻松地写完了,并直接过了两个样例。

这里我不得不抱怨几句,巴蜀二考场的机子,太烂啦啊啊啊啊啊啊啊啊啊啊啊,保存需要十秒多钟,粘贴一个文件之后,并没出现图标,重新粘贴又显示是否覆盖,最后我尝试刷新,才发现这个烂机子居然要刷新才会显示,更有莫名其妙的突然卡顿,总之用这种机子就是一种煎熬。难怪考前遇到巴蜀的黄子越,他就告诉我二考场是最恶心的机房。

回归正题,现在时间9:35

现在,身上的担子稍微轻了一点,重回第一题:
尝试用dp的方法,先做个60分,并且很容易地写完了,正准备走第三题,但又觉得不甘心,第一题我怎么能做不出来呢(一个老年蒟蒻的挣扎),于是尝试着把数据打出来,却发现一个神奇的规律,代码复杂度似乎和A+Bproblem差不多。。
但据我所知,NOIP作为一个正(lang)规(bi)的考试,应该不至于出这种看得出来就能A,看不出来就挂的题。于是我写了一个公式,又用DP重新对拍了1000以内所有数据,发现似乎没啥问题。
(这里我又要吼一句了,巴蜀的机子,你的“运行”呢???害得我要用命令行,都得从附件里面翻)
总之,暂且就写这个公式吧
时间10:25

现在,考试还有一个半小时,我怀着直接写暴力的心态,还是做第三题。其实在最开始看题目的时候,第三题我就有一个大概的思路,但我认为如果是去年第三题的难度,应该不止于是我这种蒟蒻都能秒的,于是又重新看了几遍,发现好像思路没问题。。没错我就是蒟蒻
dp[i][j]表示在i号节点,从i节点到n的最短路基础上多j的路径的方案数
转移很简单,设i点到n的最短路为len[i]
若存在一条边(u,v),其长度为val(u,v)
dp[u][j]+=dp[v][j+len[u]-len[v]-val(u,v)],初始状态dp[n][0]=1,目标状态
ki=0dp[1][i] ∑ i = 0 k d p [ 1 ] [ i ]
因为:len[u]<=len[v]+val(u,v),所以len[u]-len[v]-val(u,v)<=0
之后只要按j从小到大递推就可以了,但很容易发现,如果len[u]=len[v]+val(u,v),那么就会在j相同的情况的互相转移,就可能(间接)出现dp[x][j]+=dp[y][j],又dp[y][j]+=dp[x][j]
不难发现,此时一定有一条0权环,就意味着有无数种方案
那么很简单,
先用tarjan找出0权边组成的强联通分量,这种点的dp值均为正无穷
转移时如果转移到这种点,也视为正无穷,如果目标状态中有正无穷,就输出-1
然而。。。我想到这里时,已经是10:45了

今天题目这么容易,如果第三题炸了,那么我今天就算是完蛋了。所以,我必须稳扎稳打,追求AK的事情就交给高二的同学吧,我准备不写含0权边的那30分,毕竟spfa+dp代码并不短,再加上以我目前的精神状态,极有可能出现细节错误,小概率的高分,亦或是大概率的中等分数,我选择了后者。没想到,很顺利地写出来了,大样例过了,时间11:25,继续写满分,还是倒回去检查代码?这时我很纠结,很显然,今天的题目,估计会有很多人AK,如果我不写满分,很可能被拉出较大差距,但如果不检查代码,我又担心会出细节bug。最后,我选择检查代码,果然,发现第二题有一个小bug,就是yes和no的大小写问题(@刘湘),第三题有数组未清零,确认代码应该没问题时,已经是11:55,重新检查了文件输入输出,放心地交了。
(2018.9.14.NOIP原题重做。这次算是很轻松地实现了这道题,填上了一年前的坑。。。看着一年前写的博客。。。内心复杂。。。总之,终于能够把这场比赛放下了)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define SF scanf
#define PF printf
#define MAXN 200010
#define MAXK 55
using namespace std;
typedef long long ll;
struct node{
    int x;
    int val;
    node *nxt;  
}edge[MAXN*2];
node * heada[MAXN],*headb[MAXN],*ncnt=edge;

void add_edge(int x,int y,int val){
    ncnt++;
    ncnt->x=y;
    ncnt->val=val;
    ncnt->nxt=heada[x];
    heada[x]=ncnt;

    ncnt++;
    ncnt->x=x;
    ncnt->val=val;
    ncnt->nxt=headb[y];
    headb[y]=ncnt;
}

int cnt=0;
int st[MAXN],top,siz[MAXN],dfn[MAXN],low[MAXN],nq[MAXN],tot;
void dfs(int x){
    dfn[x]=low[x]=++cnt;
    st[++top]=x;
    for(node *v=heada[x];v!=NULL;v=v->nxt){
        if(v->val!=0)
            continue;
        int u=v->x;
        int w=v->val;
        if(dfn[u]==0){
            dfs(u); 
            low[x]=min(low[x],low[u]);
        }
        else if(nq[u]==0)
            low[x]=min(low[x],dfn[u]);
    }
    if(low[x]==dfn[x]){
        tot++;
        while(1){
            nq[st[top]]=tot;
            siz[tot]++;
            top--;
            if(st[top+1]==x)
                break;
        }
    }
}

queue<int> q;
bool inq[MAXN];
int dist[MAXN];
void spfa(int stx){
    memset(dist,0x3f3f3f3f,sizeof dist);
    dist[stx]=0;
    inq[stx]=1;
    q.push(stx);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        inq[x]=0;
        for(node *v=headb[x];v!=NULL;v=v->nxt){
            int u=v->x;
            int w=v->val;
            if(dist[u]>dist[x]+w){
                dist[u]=dist[x]+w;
                if(inq[u]==0){
                    inq[u]=1;
                    q.push(u);  
                }
            }
        }
    }
}

bool vis[MAXN][MAXK];
ll dp[MAXN][MAXK],p;
void solve(int x,int k1){
    vis[x][k1]=1;
    for(node *v=heada[x];v!=NULL;v=v->nxt){
        int u=v->x;
        int w=v->val;
        int delta=-dist[x]+dist[u]+w;
        if(k1-delta>=0){
            if(vis[u][k1-delta]==0)
                solve(u,k1-delta);
            if(siz[x]>1){
                if(dp[u][k1-delta]!=-1)
                    dp[x][k1]=-1;
            }
            else{
                if(dp[u][k1-delta]==-1)
                    dp[x][k1]=-1;
                else if(dp[x][k1]!=-1)
                    dp[x][k1]=(dp[x][k1]+dp[u][k1-delta])%p;    
            }
        }
    }
}

int n,m,k;
pair<int,int> l[MAXN];
int dis[MAXN],u,v,val;
int main(){
    int t;
    SF("%d",&t);
    while(t--){
        SF("%d%d%d%lld",&n,&m,&k,&p);
        for(int i=1;i<=n;i++)
            heada[i]=headb[i]=NULL;
        ncnt=edge;
        for(int i=1;i<=n;i++)
            dfn[i]=low[i]=nq[i]=0;
        cnt=0;
        for(int i=1;i<=n;i++)
            for(int j=0;j<=k;j++)
                dp[i][j]=vis[i][j]=0;
        for(int i=1;i<=tot;i++)
            siz[i]=0;
        tot=0;
        for(int i=1;i<=m;i++){
            SF("%d%d%d",&u,&v,&val);
            add_edge(u,v,val);  
            l[i]=make_pair(u,v);
            dis[i]=val;
        }
        for(int i=1;i<=n;i++)
            if(dfn[i]==0)
                dfs(i);
        for(int i=1;i<=n;i++)
            heada[i]=headb[i]=NULL;
        ncnt=edge;
        for(int i=1;i<=m;i++){
            u=nq[l[i].first];
            v=nq[l[i].second];
            val=dis[i];
            if(u!=v)
                add_edge(u,v,val);
        }
        int sta=nq[1];
        int ed=nq[n];
        if(sta==ed){
            PF("-1\n");
            continue;
        }
        spfa(ed);
        if(dist[sta]==0x3f3f3f3f){
            PF("0\n");
            continue;
        }
        dp[ed][0]=1;
        for(int i=0;i<=k;i++)
            solve(sta,i);
        ll ans=0;
        for(int i=0;i<=k;i++){
            if(dp[sta][i]==-1)
                ans=-1;
            else if(ans!=-1)
                ans=(ans+dp[sta][i])%p;
        }
        PF("%lld\n",ans);
    }
}

Day2

昨天的题目和同伴们交流了一下,发现的确如我所料,很多人预计AK,比我低两级的zjx同学都写的满分算法,这令我有些后悔,不过既然是自己下的决定,后悔也没意义。不过,今天的题目应该比较难了吧,否则今年AK的一群一群的,像浙江湖南等强省,估计一等奖线都有500多吧。于是,我打算第一题尽量写了之后,第二三题直接开始搞大暴力。(事实证明,我又对题目难度产生了错判)

首先照例通看全卷
第一题,确认是道水题,写了一个广搜,直接过了,时间9:15

第二题,应该是一道状压DP,但考虑到状压DP的代码复杂度,我有些犹豫,决定先看第三题。
第三题感觉似曾相识,似乎以前写过类似的,好像是个数据结构题?
这时,我感到有些无奈,无论是状压还是数据结构,都是代码复杂度极高的题,无论做哪道都有点悬,于是,又重新审视了几次这两道题,犹豫中时间已经到了9:40
直觉告诉我,必须开始写了,于是决定写第二题,但无论怎么做,时间复杂度都要超时,于是又换到第三题,这次有了一点突破,发现是可以用线段树来做,然而。。。我只想象了一下代码长度,就呵呵了。。又换回第二题,发现状态可以定义为:
dp[i][k]为在i层节点,当前层点的状态为k的最小花费,0表示未出现过,1表示在当前层,2表示在上面某层,状态转移十分恶心,不敢写。于是,就在这不停的徘徊中,时间到了10:30

嗯。。不得不写大暴力了。。。第二题暂且写了一个在40%内保证正确的贪心算法,第三题开始准备写x=1的数据,时间11:00
写完之后,又没有数据可以check,只好自己造数据(出题人出个这样的样例会死么),检查了很久。。。发现了问题。。。呵呵,算了吧,今年又炸了,写了一个30分的暴力就走了。最后检查了一下代码,就走了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值