编辑
2021-02-12
undefined
0
请注意,本文编写于 1527 天前,最后修改于 251 天前,其中某些信息可能已经过时。

目录

前言
赛前准备
一些坑点
t1 water
t2 string
t3 ball
t4 walk
小结
一些有趣的事情

前言

没想到我竟然能有幸成为参加noip2020中7k+人的一员话说不是有分就能去的吗今天考完了,特地来写个小结

赛前准备

虽然在考完csp后并不认为我能参加noip,但我还是去机房参加训练(毕竟万一进了不是很亏?)在得知noip有分就行的消息后,我更加努力地准备

于是在洛谷上做了一些题。为什么是做了一些题呢?因为我太菜了,做一道题都要很久QAQ。到了比赛前没有奥赛课的时候,我看那些高三大佬都翘课来准备,于是我周三周四的晚修请假来机房训练(还不敢翘课)。在这两个晚上,我着重搞了搞之前鸽了很久都快忘了的算法,包括但不限于线段树,树链剖分,并查集,最小生成树,最短路,倍增LCA(主要是我也忘了我搞了啥)

这里有一点经验,就是在学算法的时候不要看别人的代码,自己按照思路写印象会更深刻,捡起来也会更快。还有,写板子但时候一定要打上完备的注释(我基本一行一个),这样每次只要看看注释就知道自己当时在想什么了。最好还能有自己的blog,当然用markdown渲染也行。

最令人惊讶的是有一位大佬竟然在进场的时候前提到了gcd!然后我才发现我在准备的时候忘记搞gcd了!这波要是他不提一下我t1就凉了,妙啊!说的好像t1没凉一样

来自大佬的gcd超短模板

cpp
ll gcd(ll a,ll b) { return b? gcd(b,a%b):a; }

一些坑点

  1. 判断换行符的时候要判三个:‘\n’‘\r’‘\n\r’ (我就是因为这个洛谷上一题爆零了)
  2. printf("%s","str")会re(上面那位大佬就是因为这个noip一题爆零了,申诉还不让过,说是编译器bug人人平等???)

t1 water

题目传送门

上来一眼看到还以为是网络流!真是骇死我了。。。。。。

然后瞎写了一阵,把样例打进去,咦?怎么输入还没完就输出答案了?才发现还有中间节点QAQ

于是删掉重来,一开始就想到了bfs,但是STL的queue不会用,主要是不知道怎么取出队头。。。。。。那就手写吧!于是手写了top,empty,push和pop。

cpp
int que[1000010]; int front=0;int tail=0; int top() { return que[front]; } void pop() { if(front<tail) front++; } void push(int a) { que[tail++]=a; } bool empty() { return front==tail; }

然后又写了个分数的结构体,用构造函数初始化分母为1。由于之前重载运算符写吐了,这次直接就写了两个函数,一个搞加法,一个搞除法。

然而一输样例,发现竟然输出了负数!于是把int 改了ll,问题依旧。后来把通分时算lcm先乘后除就好了。

cpp
struct fen { ll up; ll down; fen() { down=1; } }; fen add(fen &a,fen b) { ll g=gcd(a.down,b.down); ll lcm=a.down/g*b.down; a.up*=lcm/a.down; b.up*=lcm/b.down; a.up+=b.up; a.down=lcm; g=gcd(a.up,a.down); a.up/=g; a.down/=g; return a; } void chu(fen &a,int b) { a.down*=b; ll g =gcd(a.up,a.down); a.up/=g; a.down/=g; }

这里要注意一点,这里不只是bfs就行了的,节点要先把水都排进来最后才能排出去,也就是说当一个节点的流入全部算完后才能入队去算流出。这不就是拓扑序嘛。。。。。。emmmm,调的我有亿点久QAQ

cpp
void bfs() { while(!empty()) { int now=top(); pop(); int h=head[now]; fen water; water.up=out[now].up; water.down=out[now].down; chu(water,tot[now]); while(h) { --intot[E[h].to]; add(out[E[h].to],water); if(!intot[E[h].to]) push(E[h].to); h=E[h].next; } } }

然而!数据范围是100000而我看成了1000。。。。。。又是一个100变60的伤心故事。。。。。。

ps:猥琐的ccf的猥琐数据的最后一个点爆了unsigned long long!我不觉得有哪个人会在考场上写高精度QAQ

完整代码:

cpp
#include<cstdio> #define ll long long using namespace std; ll gcd(ll a,ll b) { return b? gcd(b,a%b):a; } struct fen { ll up; ll down; fen() { down=1; } }; fen add(fen &a,fen b) { ll g=gcd(a.down,b.down); ll lcm=a.down/g*b.down; a.up*=lcm/a.down; b.up*=lcm/b.down; a.up+=b.up; a.down=lcm; g=gcd(a.up,a.down); a.up/=g; a.down/=g; return a; } ll rd() { ll num=0;bool f=false;char c=getchar(); while(c<'0'||c>'9') { if(c=='-') f=true; c=getchar(); } while(c>='0'&&c<='9') { num=(num<<1)+(num<<3)+(c^'0'); c=getchar(); } if(f) return -num; else return num; } fen out[100010]; ll tot[100010]; struct edge { int to; int next; }E[101000]; int head[101000];int cnt=0; void add_edge(int a,int b) { E[++cnt].to=b; E[cnt].next=head[a]; head[a]=cnt; } void chu(fen &a,int b) { a.down*=b; ll g =gcd(a.up,a.down); a.up/=g; a.down/=g; } int que[1000010]; int front=0;int tail=0; int top() { return que[front]; } void pop() { if(front<tail) front++; } void push(int a) { que[tail++]=a; } bool empty() { return front==tail; } int intot[100010]; void bfs() { while(!empty()) { int now=top(); pop(); int h=head[now]; fen water; water.up=out[now].up; water.down=out[now].down; chu(water,tot[now]); while(h) { --intot[E[h].to]; add(out[E[h].to],water); if(!intot[E[h].to]) push(E[h].to); h=E[h].next; } } } int final[100010]; int Ftot=0; signed main() { freopen("water.in","r",stdin); freopen("water.out","w",stdout); //int n=rd(),m=rd(); int n,m; scanf("%d",&n); scanf("%d",&m); for(int i=1;i<=n;i++) { //tot[i]=rd(); scanf("%lld",&tot[i]); if(!tot[i]) final[++Ftot]=i; for(int j=0;j<tot[i];j++) { int to; scanf("%d",&to); add_edge(i,to); ++intot[to]; } } for(int i=1;i<=m;i++) { out[i].up=1; push(i); } bfs(); for(int i=1;i<=Ftot;i++) { printf("%lld %lld\n",out[final[i]].up,out[final[i]].down); } fclose(stdin); fclose(stdout); return 0; }

t2 string

题目传送门

这种题一看就不会。我估摸着应该是dp,但是这和我不会有什么关系呢?

于是随便写了点东西没思路就走了,最后用半个小时写了只有1个字母的情况。但是复杂度有亿点大。嗯,好问题。。。。。。 关键是这种情况都没写对QAQ

cpp
#include<cstdio> using namespace std; char s[100010000]; int rdstr() { char c=getchar(); while(c<'a'||c>'z') c=getchar(); int len=0; while(c<='z'&&c>='a') { s[len++]=c; c=getchar(); } return len; } int main() { freopen("string.in","r",stdin); freopen("string.out","w",stdout); int T; scanf("%d",&T); while(T--) { int ans=0; int len=rdstr(); for(int i=1;i<len-1;i++) { int left=len-i; for(int j=1;j<=(left>>1);j++) { if(left%j) continue; int tot=left/j; for(int k=1;k<tot;k++) { if(k%2>i%2) continue; ans++; } } } printf("%d\n",ans); } fclose(stdin); fclose(stdout); return 0; }

t3 ball

难得noip竟然考spj!spj都来了,交互题还会远吗?

看到这玩意,第一反应就是不会做。不会做咋办,那就来打个暴力吧。一开始想了一大堆方案,比如从上往下找同色珠子集中到一根柱子上之类的,但都要么不可行,主要是要么不会写。到最后,想到要是能交换两根柱子上的任意两个珠子就好了。我盲猜用一根空的柱子是可以的,于是开始研究,最后还真给研究出来了!

首先先确定两颗珠子的深度大小关系,防止下面操作的时候爆柱子

cpp
if(i>j) //i<j { int c=b; b=a; a=c; c=j; j=i; i=c; }

然后把深度大的那一串(a)连带着要移动的那一颗移到空柱子上

cpp
for(int k=tot[a];k>=i;k--)//a:i~top to empty { //printf("%d %d\n",a,empty); mem(a,empty); tot[a]--; stick[empty][++tot[empty]]=stick[a][k]; ++atot; }

然后把深度小的那一串(b)连带着要移动的那一颗移到刚刚移走一串的那根柱子(a)上.由于a上移走的那一串要比这一串多,所以不会爆出柱子

cpp
for(int k=tot[b];k>=j;k--)//b:j~top to a { //printf("%d %d\n",b,a); mem(b,a); tot[b]--; stick[a][++tot[a]]=stick[b][k]; ++btot; }

然后我们就可以把原本属于a的那一颗移到b上.柱子类似栈,移动完是倒序的,所以刚刚要交换的两颗珠子现在都到了最上面

cpp
//printf("%d %d\n",empty,b);//empty: top to b mem(empty,b); stick[b][++tot[b]]=stick[empty][tot[empty]--]; --btot;

然后就要把刚刚从b上移走的那一串还回来,当然,要移到a上的那一颗先放到empty上.由于empty刚刚移走了一颗,这样做仍然不会爆柱子

cpp
//printf("%d %d\n",a,empty);//a:top to empty mem(a,empty); stick[empty][++tot[empty]]=stick[a][tot[a]--]; for(int k=0;k<btot;k++)//a:j~top to b { //printf("%d %d\n",a,b); mem(a,b); stick[b][++tot[b]]=stick[a][tot[a]--]; }

这样做完,a上要移动到的那个位置就暴露出来了,把empty上的东西移动回来就可以了

cpp
for(int k=tot[empty];k>0;k--)//empty to a { //printf("%d %d\n",empty,a); mem(empty,a); stick[a][++tot[a]]=stick[empty][tot[empty]--]; }

然后主程序就简单了,不妨认为一根柱子最下面的那一颗是这跟柱子的颜色(反正题目也没规定),前面的柱子都移动好了,从上往下一旦遇到不一样颜色的珠子,就在后面的柱子上找一个颜色一样的交换一下就行了

cpp
for(int i=1;i<n;i++) { int k=i+1; int l=m; for(int j=2;j<=m;j++) { if(stick[i][j]==stick[i][1]) continue; for(;k<=n;k++) { bool flag =false; if(!l) l=m; for(;l>0;l--) { if(stick[k][l]==stick[i][1]) { change(i,j,k,l); flag=true; break; } } if(flag) break; } } }

但是由于复杂度太大,经过了不懈随意地卡常,大样例还是跑了1.4s,不过还好答案是对的,最后骗到了40分。

完整代码:

cpp
#include<cstdio> using namespace std; int stick[60][410]; int tot[60]; int empty; int ans1[820010],ans2[820010]; int cnt=0; void mem(int a,int b) { ans1[++cnt]=a; ans2[cnt]=b; } int rd() { int num=0;char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') { num=(num<<1)+(num<<3)+(c^'0'); c=getchar(); } return num; } void change(int a,int i,int b,int j)//change a[i] and b[j] { if(i>j) //i<j { int c=b; b=a; a=c; c=j; j=i; i=c; } int atot=0,btot=0; for(int k=tot[a];k>=i;k--)//a:i~top to empty { //printf("%d %d\n",a,empty); mem(a,empty); tot[a]--; stick[empty][++tot[empty]]=stick[a][k]; ++atot; } for(int k=tot[b];k>=j;k--)//b:j~top to a { //printf("%d %d\n",b,a); mem(b,a); tot[b]--; stick[a][++tot[a]]=stick[b][k]; ++btot; } //printf("%d %d\n",empty,b);//empty: top to b mem(empty,b); stick[b][++tot[b]]=stick[empty][tot[empty]--]; --btot; //printf("%d %d\n",a,empty);//a:top to empty mem(a,empty); stick[empty][++tot[empty]]=stick[a][tot[a]--]; for(int k=0;k<btot;k++)//a:j~top to b { //printf("%d %d\n",a,b); mem(a,b); stick[b][++tot[b]]=stick[a][tot[a]--]; } for(int k=tot[empty];k>0;k--)//empty to a { //printf("%d %d\n",empty,a); mem(empty,a); stick[a][++tot[a]]=stick[empty][tot[empty]--]; } } int main() { freopen("ball.in","r",stdin); freopen("ball.out","w",stdout); int n=rd(),m=rd(); //scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { tot[i]=m; for(int j=1;j<=m;j++) { //scanf("%d",&stick[i][j]); stick[i][j]=rd(); } } empty=n+1; for(int i=1;i<n;i++) { int k=i+1; int l=m; for(int j=2;j<=m;j++) { if(stick[i][j]==stick[i][1]) continue; for(;k<=n;k++) { bool flag =false; if(!l) l=m; for(;l>0;l--) { if(stick[k][l]==stick[i][1]) { change(i,j,k,l); flag=true; break; } } if(flag) break; } } } printf("%d\n",cnt); for(int i=1;i<=cnt;i++) { printf("%d %d\n",ans1[i],ans2[i]); } fclose(stdin); fclose(stdout); return 0; }

t4 walk

题目传送门

这是人能做的题吗?输-1拿5分就回去调t1去了最后发现骗到了十分

cpp
#include<cstdio> using namespace std; int main() { freopen("walk.out","w",stdout); printf("-1"); fclose(stdout); return 0; }

小结

  1. 带零食没有用处,因为你根本就没有心思吃
  2. 比赛前多多交流很重要
  3. 大胆尝试,不会就打暴力(当然也有可能连暴力都打不出来)
  4. 心态很重要,部分分还是可以拿一下的
  5. 眼睛是个好东西QAQ

一些有趣的事情

训练的时候,写完快读突发奇想

众所周知在main函数外面是不能执行函数的,但是把函数的返回值赋给变量竟然还就可以了!

至于怎么结束程序不re,不是可以用exit(0)嘛~

甚至都不需要main函数,有个main数组或是main指针竟然还能顺利通过编译!

感受一下不用main就能执行的a+b(虽然洛谷上会CE)

本文作者:GBwater

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!