day1

road

题意

有n(\(n \leq 10^5\))个数\(a_1,a_2,...,a_n\)排成一排,每次可以选择一段大于零的数减一,问最少几次把所有数减为0。

题解

先想到一个简单的策略:当处理到区间\([l,r]\)时,找出其中的最小值出现的位置\(k\),对这个区间进行\(a_k\)次区间减一,再分别处理\([l,k-1]\)和\([k+1,r]\)。

但是区间\([l,r]\)要是有很多个最小值,出现位置为\(k_1,k_2,\)...\(,k_p\),该怎么办?想必是没什么影响的。对这个区间进行最小值次区间减一后,\(k_1,k_2,\)...\(,k_p\)上的数都变成0,选其中的哪一个作为断点都没什么区别。

要想维护区间最值,用st表就行了。

代码

#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<stack>
#include<vector>
#include<queue>
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(int i=(x);i>=(y);--i)
#define maxn 100010
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
if(x==0){putchar('0'),putchar('\n');return;}
if(x<0){putchar('-'),x=-x;}
char ch[20];int f=0;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('\n');
}
int n,d[maxn],st[20][maxn],lg[maxn],ans=0;
int MIN(int l,int r)
{
int len=r-l+1;
return d[st[lg[len]][l]]<=d[st[lg[len]][r-(1<<lg[len])+1]]?st[lg[len]][l]:st[lg[len]][r-(1<<lg[len])+1];
}
void work(int l,int r,int lst)
{
int minx=MIN(l,r);
ans+=(d[minx]-lst);
if(l<=minx-1)work(l,minx-1,d[minx]);
if(minx+1<=r)work(minx+1,r,d[minx]);
return;
}
int main()
{
n=read();
rep(i,1,n)d[i]=read(),st[0][i]=i;
lg[0]=-1;
rep(i,1,n)lg[i]=lg[i>>1]+1;
rep(k,1,lg[n])for(int i=1;i+(1<<k)-1<=n;i++)
st[k][i]=d[st[k-1][i]]<=d[st[k-1][i+(1<<(k-1))]]?st[k-1][i]:st[k-1][i+(1<<(k-1))];
work(1,n,0);
write(ans);
return 0;
}

money

题意

有\(n\)(\(n\leq 100\))个数\(a_1,a_2,\)...\(,a_n\)(\(\forall i\in[1,n],a_i\leq25000\))。

定义“凑出”是:当存在一个\(n\)个数的数列\(t_1,t_2,\)...\(,t_n\)使\(\sum_{i=1}^n a_i*t_i \space= k\)时,k能被这n个数凑出。

求最小的数\(m\),使存在m个数\(b_1,\)...\(,b_m\),这些数能凑出\(a_1,\)...\(,a_n\)能凑出的所有数,凑不出\(a_1,\)...\(,a_n\)凑不出的所有数。

共\(T\)(\(T\leq20\))组输入。

题解

把这\(n\)个数中所有能被\(n\)个数中的其它数凑出的删掉。

正确性:假设数\(x\)能被\(n\)个数中的其它数凑出,那么\(x\)能凑出的数一定能被凑出\(x\)的数凑出,删掉\(x\)后,原来能凑出的数还能被凑出。

最优性:那么这个可以用反证法来证明,假设存在一种删去了p个数,又添加了q(q<p)个数的方法比只删数更优。为了让方法更优,添加的数一定不能被未删除的数凑出。但是,这样添加的数就是原来凑不出的数,现在能凑出了,与题意不符。所以添加数不会使答案更优,只删掉能被凑出的数就行了。

需要判断每个数能否被凑出,这是个完全背包问题。

代码

#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<stack>
#include<vector>
#include<queue>
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(int i=(x);i>=(y);--i)
#define maxk 25010
#define maxn 110
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
if(x==0){putchar('0'),putchar('\n');return;}
if(x<0){putchar('-'),x=-x;}
char ch[20];int f=0;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('\n');
}
int yes[maxk],t,maxpos,a[maxn],n,ans,maxlim;
int main()
{
t=read();
while(t--)
{
n=read(),ans=n;maxlim=maxpos=0;
rep(i,1,n)a[i]=read(),maxlim=max(maxlim,a[i]);
rep(i,1,maxlim)yes[i]=0;yes[0]=1;
sort(a+1,a+n+1);
rep(i,1,n)
{
if(yes[a[i]]){ans--;continue;}
rep(j,0,maxpos)
{
if(j+a[i]>maxlim)break;
if(yes[j])yes[j+a[i]]=1,maxpos=max(maxpos,j+a[i]);
}
}
write(ans);
}
return 0;
}

track

题意

有一棵有\(n\)(\(n\leq5*10^4\))的树,有边权。

要把这棵树拆成\(m\)条链,链之间可以有公共点,但不能有公共边。

求一种拆分方案,使边权和最小的链边权和最大。

题解

“最大值最小”显然是需要二分的,然后就是判断这棵树能否拆成不少于m条边权和大于等于k的链。

这样问题就转换成:对于每个点,选若干对儿子,使得每一对中,两个儿子向下连的直链长度,加上该点到两个儿子的边大于等于\(k\),在剩下儿子中选择向下的直链长度加该点到这个儿子的边长度最长的作为该点向下的直链。需要找出一种方法使选出的儿子对的个数尽可能多,在此基础上,该点向下的直链长度尽可能长。

\(l_i\)表示在处理完\(i\)的子树后,点\(i\)向下连的链最长有多长。

在计算完点\(u\)的所有儿子\(v_i\)的\(l_{v_i}\)后,将所有儿子按\(l_{v_i}+i到这个儿子的距离\)排序,第\(j\)个记作\(a_j\)。

这样问题就变成在单调不降数列\(a_1,\)...\(,a_p\)中,选出若干对数,同一个数只能被选一次,使每一对的和大于等于\(k\),选出的数对的个数尽可能多,在此基础上,剩下的数中最大的数尽可能大。

如果只用考虑数对个数最多,就可以这样:找出位置\(q\)使\(k\leq a_q+a_{q+1}\)且\(k\geq a_{q-1}+a_q\),那么对于小于\(a_q\)的数,只有在大于\(a_q\)的数中才能找到与它相加大于\(k\)的。那么就可以从\(a_{q-1}\)到\(a_1\),判断是否存在\(i\)使\(a_i\)未被选且\(当前数+a_i\geq k\),如果有,就取符合条件的最小的\(a_i\)。判断完所有小于\(a_q\)的数后,未被选的大于等于\(a_q\)的数直接两两配对就行了。以上过程的时间复杂度是\(O(n)\)的,可以维护一个指针\(i\),初始位置为\(q\),每当\(当前数+a_i\leq k\)时,右移\(i\),因为随着当前数减小,符合条件的与它配对的数是不断增大的。

那要是要考虑在选出数对个数最多的基础上,剩下的数的最大值尽可能大呢?看上去没有简单的贪心策略。先有个暴力的想法:枚举最后剩下的数中的最大值,求删掉枚举的数后的数列中最多能选出多少对。会发现删掉较大的数后的答案肯定不会比删掉较小的数的答案更大,也就是有单调性。那么只要先二分出剩下的数的最大值,再贪心求出删掉该数后最多能凑出几对就行了。

代码

#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<stack>
#include<vector>
#include<queue>
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(int i=(x);i>=(y);--i)
#define maxn 50010
#define maxm 100010
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
if(x==0){putchar('0'),putchar('\n');return;}
if(x<0){putchar('-'),x=-x;}
char ch[20];int f=0;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('\n');
}
int num,ans,mid,n,m,fa[maxn],fir[maxn],nxt[maxm],v[maxm],w[maxm],sons;
int tofa[maxn],son[maxn],tmp[maxn],tn,mk[maxn],len[maxn],L=10001,R,cnt,tmpnum;
void ade(int u1,int v1,int w1){v[cnt]=v1,w[cnt]=w1,nxt[cnt]=fir[u1],fir[u1]=cnt++;}
int getx(int u){return len[u]+tofa[u];}
bool cmp(int x,int y){return getx(x)<getx(y);}
void check(int lim)
{
tmpnum=tn=0;int pos=0;
rep(i,1,sons)if(i!=lim)tmp[++tn]=son[i];
rep(i,1,tn-1)if(getx(tmp[i])+getx(tmp[i+1])>=mid){pos=i;break;}
if(pos==0){return;}
else
{
rep(i,1,tn)mk[i]=0;
int b=pos+1,c=0;
dwn(a,pos,1)
{
if(b>tn)break;
while(b<tn&&getx(tmp[a])+getx(tmp[b])<mid)b++;
if(getx(tmp[a])+getx(tmp[b])>=mid)mk[a]=mk[b]=1,b++,tmpnum++;
else break;
}
rep(i,pos+1,tn)if(!mk[i])c++;
tmpnum+=c/2;
}
}
void getans(int u)
{
for(int k=fir[u];k!=-1;k=nxt[k])if(v[k]!=fa[u]){getans(v[k]);}
sons=0;
for(int k=fir[u];k!=-1;k=nxt[k])if(v[k]!=fa[u]){if(getx(v[k])>=mid)num++;else son[++sons]=v[k];}
if(sons==0){len[u]=0;return;}
sort(son+1,son+sons+1,cmp);
int pos=0;
rep(i,1,sons-1)if(getx(son[i])+getx(son[i+1])>=mid){pos=i;break;}
if(pos==0){len[u]=getx(son[sons]);return;}
else
{
int l=1,r=sons,ansnum=0,anslen=0;
check(0);ansnum=tmpnum;
while (l<=r)
{
int mi=(l+r)>>1;check(mi);
if(tmpnum>ansnum||(tmpnum==ansnum&&getx(son[mi])>anslen))ansnum=tmpnum,anslen=getx(son[mi]),l=mi+1;
else r=mi-1;
}
num+=ansnum,len[u]=anslen;
}
return;
}
int check()
{
num=0;
rep(i,1,n)len[i]=0;
getans(1);
if(num>=m)return 1;
else return 0;
}
void getfa(int u)
{
for(int k=fir[u];k!=-1;k=nxt[k])
if(v[k]!=fa[u])tofa[v[k]]=w[k],fa[v[k]]=u,getfa(v[k]);
}
int main()
{
n=read(),m=read();
memset(fir,-1,sizeof(fir));
rep(i,1,n-1){int x=read(),y=read(),z=read();ade(x,y,z),ade(y,x,z);L=min(L,z),R+=z;}
getfa(1);
while(L<=R)
{
mid=((L+R)>>1);int f=check();
if(f)ans=max(mid,ans),L=mid+1;
else R=mid-1;
}
write(ans);
fclose(stdout);
return 0;
}

day2

travel

题意

有一棵有\(n\)(\(n\leq 5000\))个点的树或基环外向树。

每个点只能走一次,求字典序最小的DFS序。

题解

如果是树,就贪心地先走编号更小的儿子。

有环的话,就枚举删掉环上的哪条边,变成树。

需要注意的是,要在枚举删边之前将每个点的儿子排序。要不然\(O(n^2 log n)\)会被卡。

代码

#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define maxn 5010
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
if(x==0){putchar('0'),putchar(' ');return;}
int f=0;char ch[20];
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar(' ');
return;
}
int to[maxn][maxn],tl[maxn],qx[maxn],qy[maxn],num,n,m,nox,noy,vis[maxn],ans[maxn],tmp[maxn],tmpn,cntq,yes;
void work(int u)
{
vis[u]=1,tmp[++tmpn]=u;
rep(k,1,tl[u])if(!vis[to[u][k]]&&(u!=nox||to[u][k]!=noy)&&(u!=noy||to[u][k]!=nox))work(to[u][k]);
}
void getcir(int u,int fa)
{
vis[u]=1;
rep(k,1,tl[u])
{
if(to[u][k]!=fa)
{
if(vis[to[u][k]])
{
yes=1;
qx[++cntq]=u,qy[cntq]=to[u][k];
return;
}
else
{
getcir(to[u][k],u);
if(yes==1)
{
if(u==qy[1])yes=-1;
qx[++cntq]=u,qy[cntq]=to[u][k];
return;
}
if(yes)return;
}
}
}
}
void upd()
{
if(ans[1]!=0)
{
rep(i,1,n)
{
if(tmp[i]<ans[i])break;
if(tmp[i]>ans[i])return;
}
}
rep(i,1,n)ans[i]=tmp[i];
return;
}
int main()
{
n=read(),m=read();
rep(i,1,m){int x=read(),y=read();to[x][++tl[x]]=y,to[y][++tl[y]]=x;}
rep(i,1,n)sort(to[i]+1,to[i]+tl[i]+1);
if(n-1==m)
{
work(1);
rep(i,1,n)write(tmp[i]);
return 0;
}
getcir(1,0);
rep(i,1,cntq)
{
nox=qx[i],noy=qy[i];tmpn=0;
rep(i,1,n)vis[i]=0;
work(1);
upd();
}
rep(i,1,n)write(ans[i]);
return 0;
}

game

自闭了,不写。

defence

自闭了,不写。

总结

爆零就完事儿了

我记得有一次我接了个任务:砍五只大野猪

我想:我都是个上位猎人了,单挑火龙一家、两只雷狼都不带掉血的,五只大野猪算什么?

于是拿着古结云大剑,啥都没穿,啥都没拿就去了

结果被秋名山猪神安排得明明白白的,虽然完成了,但是过程不太愉快

可能有一些方面是因为轻敌,但主要还是走位不够灵活、预判不够精准及时、对地形的运用不够好导致的

活用道具、合理配置装备,确实是比较重要的

但要想在技术上更进一步,就必须尝试脱离这些东西,只靠自己的脑子和手战斗

在这种情况下能够通关后,也该尝试着在通关速度上更进一步,或者用不常使用的武器(大剑、片手以外的所有武器。。。)通关

\(\color{white}{\text{事实上,这个任务上位猎人拿脚玩都没问题……所以,你知道我只是在比喻,对吧?}}\)

我什么时候才能成为能裸装零针迅龙的大剑使呢?

并不对劲的noip2018的更多相关文章

  1. [NOIP2018]普及组游记

    想不到自己还有机会写游记 ——sysky 考完一个月后 DAY -INF 报名 还为了拍照下载了一个PS 特地把自己P白了一点233 花里胡哨得提交了rg.noi.cn DAY -14~-2 停课集训 ...

  2. $NOIP2018$ 暴踩全场计划实施方案

    \(NOIP2018\) 暴踩全场计划实施方案 改完题辣!该更博辣!(这么激动干嘛反正又没人看) 我要取一个霸气的名字.于是就这样了.原本打算是暴踩yyb计划实施方案的(来啊互相伤害啊) 信心流选手就 ...

  3. [题解]NOIP2018(普及组)T1标题统计(title)

    NOIP2018(普及组)T1标题统计(title) 题解 [代码(AC)] #include <iostream> #include <cstdio> #include &l ...

  4. NOIP2018:The First Step

    NOIP2018 RP=Ackermann(4,3) Day 0 日常不想做题也不知道要写什么qwq Day 1 接到$smy$巨佬的催更私信于是来更了(原本准备咕掉的) 最开始的策略是准备总览题目, ...

  5. NOIP2018普及初赛解析

    2018年第二十四届全国青少年信息学奥林匹克联赛初赛普及组真题解析 一.单项选择题 1. 以下哪一种设备属于输出设备:(D) A.扫描仪 _B.键盘C. 鼠标 _D. 打印机 解析:送分题,前三个都是 ...

  6. NOIP2018 游记 QAQ

    写在前面: 本人初三党.NOIP前两个月不好好停课搞信竞愣是要搞文化课.于是,期中考与NOIP一起凉凉[微笑] 本人写的第一篇NOIP游记,各位大佬们随便看一看就好 Day -n 初赛71,竟然跟wx ...

  7. NOIP2018总结

    细细想来,学习OI也有4年多的时间了,今年已经是第二次参加noip提高组了,有必要写点什么了 NOIP2018 记得在天刚蒙蒙亮的时候走进70中,这是第四次了,但紧张只增不减,在刺骨的寒风下身体微微发 ...

  8. NOIP2018游记-退役之战

    \(Day\ 0\) 从火车站下来坐地铁\(1\)小时,再乘公交车到酒店,还要帮队里一个断腿大佬搬东西,累死我了.. 到酒店就快\(5\)点了,想打个牌也没时间. 酒店的房间很不错,空间大又干净,后来 ...

  9. Luogu5021 [NOIP2018]赛道修建

    Luogu5021 [NOIP2018]赛道修建 一棵大小为 \(n\) 的树,边带权.选 \(m\) 条链使得长度和最小的链最大. \(m<n\leq5\times10^4\) 贪心,二分答案 ...

随机推荐

  1. POJ-1067取石子游戏,威佐夫博弈范例题/NYOJ-161,主要在于这个黄金公式~~

    取石子游戏 Time Limit: 1000MS   Memory Limit: 10000K              Description 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取 ...

  2. git push ‘No refs in common and none specified’doing nothing问题解决

    git push ‘No refs in common and none specified’doing nothing问题解决 输入git push origin master即可解决问题

  3. ISAPI映射路径错误,导致K3Cloud打不开。

    今天一个同事说她的K3Cloud打不开,一看是页面报500错误,具体信息看图片: 问题: ISAPI配置的映射路径错了,多了个反斜线. 解决办法: 在IIS管理器中找到ISAPI筛选器,删除掉就行了.

  4. Codeforces827D. Best Edge Weight

    $n \leq 2e5,m \leq 2e5$的有边权图,对每条边问:不改其他边的情况下这条边最多能是多少使得他一定在所有最小生成树上,如果无穷大输出-1. 典型题+耗时题,CF上的绝望时刻..打VP ...

  5. CoolCTO - 创业者的技术合伙人

    CoolCTO - 创业者的技术合伙人

  6. SpringMVC get请求中文乱码

    针对GET请求的编码问题,则需要改tomcat的server.xml配置文件,如下: 原 <Connector connectionTimeout="20000" port= ...

  7. eclipse提速01 - 禁用不常用的eclipse启动插件

    会不断更新,需要的收藏 禁用不常用的eclipse启动插件(当然如果不需要可以卸载)

  8. PLSQL安装资料

    一.plsql developer 注册码 plsql developer 10 注册码 product code :4v6hkjs66vc944tp74p3e7t4gs6duq4m4szbf3t38 ...

  9. Jmeter参数Parameters和Body Data区别

    1.如图: 2.有文章说,Parameters是get的参数:Body Data是post的参数:get的参数存在于url中,post的参数存在于body中:   但是我使用jmeter3.3版本测试 ...

  10. 利用WiFi Pineapple Nano渗透客户端获取SHELL

    前言: 前两篇文章介绍了The WiFi Pineapple Nano设备的一些主要功能模块,例如PineAP.SSLsplit和Ettercap等.今天给大家实际场景演示下如何利用Pineapple ...