考前一周
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分的暴力就走了。最后检查了一下代码,就走了。