并不对劲的noip2018
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的更多相关文章
- [NOIP2018]普及组游记
想不到自己还有机会写游记 ——sysky 考完一个月后 DAY -INF 报名 还为了拍照下载了一个PS 特地把自己P白了一点233 花里胡哨得提交了rg.noi.cn DAY -14~-2 停课集训 ...
- $NOIP2018$ 暴踩全场计划实施方案
\(NOIP2018\) 暴踩全场计划实施方案 改完题辣!该更博辣!(这么激动干嘛反正又没人看) 我要取一个霸气的名字.于是就这样了.原本打算是暴踩yyb计划实施方案的(来啊互相伤害啊) 信心流选手就 ...
- [题解]NOIP2018(普及组)T1标题统计(title)
NOIP2018(普及组)T1标题统计(title) 题解 [代码(AC)] #include <iostream> #include <cstdio> #include &l ...
- NOIP2018:The First Step
NOIP2018 RP=Ackermann(4,3) Day 0 日常不想做题也不知道要写什么qwq Day 1 接到$smy$巨佬的催更私信于是来更了(原本准备咕掉的) 最开始的策略是准备总览题目, ...
- NOIP2018普及初赛解析
2018年第二十四届全国青少年信息学奥林匹克联赛初赛普及组真题解析 一.单项选择题 1. 以下哪一种设备属于输出设备:(D) A.扫描仪 _B.键盘C. 鼠标 _D. 打印机 解析:送分题,前三个都是 ...
- NOIP2018 游记 QAQ
写在前面: 本人初三党.NOIP前两个月不好好停课搞信竞愣是要搞文化课.于是,期中考与NOIP一起凉凉[微笑] 本人写的第一篇NOIP游记,各位大佬们随便看一看就好 Day -n 初赛71,竟然跟wx ...
- NOIP2018总结
细细想来,学习OI也有4年多的时间了,今年已经是第二次参加noip提高组了,有必要写点什么了 NOIP2018 记得在天刚蒙蒙亮的时候走进70中,这是第四次了,但紧张只增不减,在刺骨的寒风下身体微微发 ...
- NOIP2018游记-退役之战
\(Day\ 0\) 从火车站下来坐地铁\(1\)小时,再乘公交车到酒店,还要帮队里一个断腿大佬搬东西,累死我了.. 到酒店就快\(5\)点了,想打个牌也没时间. 酒店的房间很不错,空间大又干净,后来 ...
- Luogu5021 [NOIP2018]赛道修建
Luogu5021 [NOIP2018]赛道修建 一棵大小为 \(n\) 的树,边带权.选 \(m\) 条链使得长度和最小的链最大. \(m<n\leq5\times10^4\) 贪心,二分答案 ...
随机推荐
- python014 Python3 迭代器与生成器
Python3 迭代器与生成器迭代器迭代是Python最强大的功能之一,是访问集合元素的一种方式..迭代器是一个可以记住遍历的位置的对象.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结 ...
- Leetcode 307.区域检索-数组可修改
区域检索-数组可修改 给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点. update(i, val) 函数可以通过将下标为 i 的 ...
- php 时间 数组替换
//随机数和时间//随机数生产器//echo rand();//echo "<br>";//生成某个范围的随机数//echo rand(0,10);//echo &qu ...
- HDU2069-Coin Change
Coin Change 这题题意和UVA674很像,但加了一个限制条件不能超过100个硬币.于是我们可以用d[i][j]来表示硬币数量为i时金钱总数为j时的方法总数,总钱不能超过250. const ...
- 【数学+枚举】OpenJ_POJ - C17J Pairs
https://vjudge.net/contest/171652#problem/J [题意] 问有多少个正整数对(x,y),使得存在正整数p,q满足 1 <= T <= 15 1 &l ...
- 最大数(cogs 1844)
[题目描述] 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作.语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值.限制:L不超过当前数列的长度. 2. 插入操作 ...
- 匿名函数--lambda函数
匿名函数 匿名函数:为了解决一些功能很简单的需求而设计的一句话函数 (python对匿名函数支持有限,只有一些简单的条件下可以用匿名函数) 匿名函数固定格式: func = lambda *args: ...
- 【Java源码】集合类-ArrayList
一.类继承关系 public class ArrayList<E> extends AbstractList<E> implements List<E>, Rand ...
- MongoDB学习day07--mongoose入门,数据库增删改查,默认参数,模块化
一.mongoose介绍 Mongoose 是在 node.js 异步环境下对 mongodb 进行便捷操作的对象模型工具. Mongoose 是 NodeJS 的驱动, 不能作为其他语言的驱动. M ...
- ZOJ 3316 Game 一般图最大匹配带花树
一般图最大匹配带花树: 建图后,计算最大匹配数. 假设有一个联通块不是完美匹配,先手就能够走那个没被匹配到的点.后手不论怎么走,都必定走到一个被匹配的点上.先手就能够顺着这个交错路走下去,最后一定是后 ...