记NOIP2018
day0
中午在机房水了一波出发,坐了一下午的车,5点到了大门对面的红旗宾馆。南山中学的和我们住在一个宾馆里面,Z教练似乎同他们关系很好,见面还打招呼。
红旗宾馆附近特别偏僻,出门就是修路的工地,后面一个小巷里有很多卖面的,刀削面、燃面、拉面……但是这些馆子,大多卫生条件不好。W学霸带我们在附近转了一圈,没看到什么好吃的,却把挂着治疗妇女孕育问题的横幅的社区卫生服务中心和空无一人的派出所找到了。按W学霸的意思,“这下考试不用慌了,把医院和派出所的位置摸熟了。”最后我们还是回到了那个巷子里吃了一碗燃面,味道还行。
晚上把洛谷的讲义从头到尾仔细翻了一遍,感觉应该没问题。期间Z教练找我们谈话,说了一些注意事项并鼓励我们建立信心。然后去附近的红旗超市买了一些零食以及明天的早饭。那超市不错,商品比较齐全,我买了一条巧克力备用,到现在我还没吃(说着就吃了)。半夜起来上了一道厕所,感觉那几天有点犯肠炎。
day1
早上起来没怎么睡醒,吃了昨天晚上买的面包,泡了香飘飘。然后在大厅里面等,坐在靠门的沙发上,我没穿秋裤,所以有点冷,很不舒服。然后人齐 了出发,路上重睹1年未见的罗马风格的电子科大校园,虽然风景不错,但是对于即将考试的我们,还有什么心思欣赏呢?大有“风萧萧兮易水寒”的感觉。路上Z教练告诉我要稳,“要把3题都A非常难,不要目标太高了,反而好高骛远”。
day1三道题还算简单。
T1
我想出一个线段树做法,维护最大最小值,每次二分查找连续全0(全非0)段,若是全非0段答案就加上改段的最小值,该段减去此值。复杂度我也不知道,大概若一段为峡谷式的坑有\(d\)种值,我就要做\(d\)次,而做一次\(O(\log^2 n)\),然后总共\(\frac{n}{d}\)段,所以复杂度\(O(n \log^2 n)\)?不太清楚。反正是A了,跑得还挺快。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<algorithm>
#include<cmath>
//#include<ctime>
#define rg register
#define il inline
template<class T>il T read(rg T&x)
{
rg T data=0;
rg int w=1;
rg char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
{
data=data*10+ch-'0';
ch=getchar();
}
return x=data*w;
}
using namespace std;
typedef long long ll;
const int MAXN=1e5+7;
int n;
struct SegTree
{
int minv[MAXN<<2],maxv[MAXN<<2];
int addv[MAXN<<2];
#define lson (now<<1)
#define rson (now<<1|1)
il void pushup(rg int now)
{
minv[now]=min(minv[lson],minv[rson]);
maxv[now]=max(maxv[lson],maxv[rson]);
}
il void pushdown(rg int now)
{
if(addv[now])
{
minv[lson]+=addv[now],
maxv[lson]+=addv[now],
addv[lson]+=addv[now];
minv[rson]+=addv[now],
maxv[rson]+=addv[now],
addv[rson]+=addv[now];
addv[now]=0;
}
}
il void build(rg int now,rg int l,rg int r)
{
// addv[now]=0;
if(l==r)
{
maxv[now]=read(minv[now]);
return;
}
rg int m=(l+r)>>1;
build(lson,l,m);
build(rson,m+1,r);
pushup(now);
}
il int qmin(rg int now,rg int l,rg int r,rg int ql,rg int qr)
{
if(ql<=l&&r<=qr)
{
return minv[now];
}
pushdown(now);
rg int m=(l+r)>>1;
if(qr<=m)
return qmin(lson,l,m,ql,qr);
if(ql>=m+1)
return qmin(rson,m+1,r,ql,qr);
return min(qmin(lson,l,m,ql,qr),qmin(rson,m+1,r,ql,qr));
}
il int qmax(rg int now,rg int l,rg int r,rg int ql,rg int qr)
{
if(ql<=l&&r<=qr)
{
return maxv[now];
}
pushdown(now);
rg int m=(l+r)>>1;
if(qr<=m)
return qmax(lson,l,m,ql,qr);
if(ql>=m+1)
return qmax(rson,m+1,r,ql,qr);
return max(qmax(lson,l,m,ql,qr),qmax(rson,m+1,r,ql,qr));
}
il void add(rg int now,rg int l,rg int r,rg int ql,rg int qr,rg int v)
{
if(ql<=l&&r<=qr)
{
minv[now]+=v,
maxv[now]+=v,
addv[now]+=v;
return;
}
pushdown(now);
rg int m=(l+r)>>1;
if(ql<=m)
add(lson,l,m,ql,qr,v);
if(qr>=m+1)
add(rson,m+1,r,ql,qr,v);
pushup(now);
}
}T;
il void find1(rg int&l,rg int&r)
{
rg int L=l,R=n,res;
while(L<=R)
{
rg int M=(L+R)>>1;
if(T.qmin(1,1,n,l,M)>0)
{
res=M;
L=M+1;
}
else
{
R=M-1;
}
}
r=res;
}
il void find0(rg int&l,rg int&r)
{
rg int L=l,R=n,res;
while(L<=R)
{
rg int M=(L+R)>>1;
if(T.qmax(1,1,n,l,M)==0)
{
res=M;
L=M+1;
}
else
{
R=M-1;
}
}
r=res;
}
int main()
{
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
read(n);
T.build(1,1,n);
// cerr<<"build end"<<endl;
rg int ans=0;
while(T.qmax(1,1,n,1,n)>0)
{
// cerr<<"cur max="<<T.qmax(1,1,n,1,n)<<endl;
// system("pause");
for(rg int l=1,r;l<=n;l=r+1)
{
// cerr<<"l="<<l<<endl;
if(T.qmin(1,1,n,l,l)>0)
{
// cerr<<"find1"<<endl;
find1(l,r);
rg int delta=T.qmin(1,1,n,l,r);
ans+=delta;
T.add(1,1,n,l,r,-delta);
// cerr<<"r="<<r<<" delta="<<delta<<endl;
}
else
{
// cerr<<"find0"<<endl;
find0(l,r);
// cerr<<"r="<<r<<endl;
}
}
}
printf("%d\n",ans);
// fprintf(stderr,"%d\n",clock());
return 0;
}
后来发现别人的代码那么短……我真是太菜了。
所以我们的贪心策略是:
若a[i]>a[i-1],计数器sum+=a[i]-a[i-1];
那么为什么这样贪心是对的呢?贪心证明
假设现在有一个坑,但旁边又有一个坑。
你肯定会选择把两个同时减1;
那么小的坑肯定会被大的坑“带着”填掉。
大的坑也会减少a[i]-a[i-1]的深度,可以说是“免费的”;
所以这样贪心是对的;
#include<bits/stdc++.h>
using namespace std;
int n,a[100005];
long long ans=0;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=2;i<=n;i++) if(a[i]>a[i-1]) ans+=a[i]-a[i-1];
cout<<ans+a[1];
return 0;
}
T2
我一看题就想到小凯的诱惑上去了。然后写了一波exgcd,调了好久。最后发现有3个抵消1个的情况,我才反应过来打个背包就行了。然后用bitset实现,算了一下时间复杂度似乎是卡着上限了,5e7,但是我们有信仰,不虚。时间复杂度\(O(T n a_i)\),有没有除64我不知道。然后也A了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<algorithm>
#include<cmath>
#include<bitset>
#define il inline
#define rg register
template<class T>il T read(rg T&x)
{
rg T data=0;
rg int w=1;
rg char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
{
data=data*10+ch-'0';
ch=getchar();
}
return x=data*w;
}
using namespace std;
typedef long long ll;
bitset<30000>s;
const int MAXN=200;
int n,a[MAXN],mx;
bool vis[MAXN];
int main()
{
freopen("money.in","r",stdin);
freopen("money.out","w",stdout);
rg int T;
read(T);
while(T--)
{
read(n);
mx=-1;
for(rg int i=1;i<=n;++i)
{
read(a[i]);
vis[i]=0;
mx=max(mx,a[i]);
}
// cerr<<"mx="<<mx<<endl;
sort(a+1,a+n+1);
n=unique(a+1,a+n+1)-a-1;
fill(vis+1,vis+n+1,0);
for(rg int i=1;i<=mx;++i)
s[i]=0;
s[0]=1;
for(rg int i=1;i<=n;++i)
if(!vis[i])
{
if(s[a[i]])
{
vis[i]=1;
continue;
}
for(rg int j=a[i];j<=mx;++j)
s[j]=s[j]|s[j-a[i]];
}
rg int ans=0;
for(rg int i=1;i<=n;++i)
if(!vis[i])
{
// cerr<<a[i]<<" remain"<<endl;
++ans;
}
printf("%d\n",ans);
}
return 0;
}
T3
这题我没有什么好想法,打了一条链,m=1,菊花图的情况,得了45分。本来预计得分50。
namespace
打子任务真好用。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<algorithm>
#include<cmath>
#define il inline
#define rg register
template<class T>il T read(rg T&x)
{
rg T data=0;
rg int w=1;
rg char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
{
data=data*10+ch-'0';
ch=getchar();
}
return x=data*w;
}
using namespace std;
typedef long long ll;
const int MAXN=5e4+7;
int n,m;
struct edge
{
int nx,to,w;
}e[MAXN<<1];
int head[MAXN],ecnt;
il void addedge(rg int x,rg int y,rg int w)
{
e[++ecnt].to=y,e[ecnt].w=w;
e[ecnt].nx=head[x],head[x]=ecnt;
}
namespace t1
{
int ans;
int f[MAXN];
il void dfs(rg int x,rg int fa)
{
for(rg int i=head[x];i;i=e[i].nx)
{
rg int y=e[i].to,w=e[i].w;
if(y==fa)
continue;
dfs(y,x);
ans=max(ans,f[x]+f[y]+w);
f[x]=max(f[x],f[y]+w);
}
}
il void solve()
{
ans=0;
dfs(1,0);
printf("%d\n",ans);
}
}
namespace t2
{
int deg[MAXN];
int w[MAXN],len;
il void dfs(rg int x,rg int fa)
{
for(rg int i=head[x];i;i=e[i].nx)
{
rg int y=e[i].to,w=e[i].w;
if(y==fa)
continue;
t2::w[++len]=w;
dfs(y,x);
}
}
il bool judge(rg int M)
{
rg int cnt=0,cursum=0;
for(rg int i=1;i<=len;++i)
{
cursum+=w[i];
if(cursum>=M)
{
++cnt;
cursum=0;
}
}
return cnt>=m;
}
il void solve()
{
rg int L=1e9,R=0;
for(rg int i=1;i<=ecnt;++i)
{
++deg[e[i].to];
L=min(L,e[i].w);
R+=e[i].w;
}
rg int s;
for(rg int i=1;i<=n;++i)
if(deg[i]==1)
{
s=i;
break;
}
dfs(s,0);
rg int ans;
while(L<=R)
{
rg int M=(L+R)>>1;
if(judge(M))
{
ans=M;
L=M+1;
}
else
R=M-1;
}
printf("%d\n",ans);
}
}
namespace t3
{
int w[MAXN],len;
bool judge(int M)
{
// cerr<<"judging "<<M<<endl;
int pos=lower_bound(w+1,w+len+1,M)-w,cnt=len-pos+1;
// cerr<<"pos="<<pos<<" cnt="<<cnt<<endl;
for(int i=pos-1,j=1;i>j;--i)
{
while(j<i&&w[j]+w[i]<M)
++j;
if(j<i)
++cnt;
}
return cnt>=m;
}
void solve()
{
for(int i=head[1];i;i=e[i].nx)
w[++len]=e[i].w;
sort(w+1,w+len+1);
rg int L=1e9,R=0;
for(rg int i=1;i<=ecnt;++i)
{
L=min(L,e[i].w);
R+=e[i].w;
}
int ans;
while(L<=R)
{
int M=(L+R)>>1;
if(judge(M))
{
ans=M;
L=M+1;
}
else
R=M-1;
}
printf("%d\n",ans);
}
}
int main()
{
freopen("track.in","r",stdin);
freopen("track.out","w",stdout);
read(n);read(m);
rg bool chain=1,flower=1;
for(rg int i=1;i<n;++i)
{
int x,y,w;
read(x);read(y);read(w);
addedge(x,y,w);
addedge(y,x,w);
if(y!=x+1)
chain=0;
if(x!=1)
flower=0;
}
if(m==1)
t1::solve();
else if(chain)
t2::solve();
else if(flower)
t3::solve();
else if(m==n-1)
{
int minv=1e9;
for(int i=1;i<=ecnt;++i)
minv=min(minv,e[i].w);
printf("%d\n",minv);
}
else
{
puts("200305");
}
return 0;
}
中午考完后H同学妈妈带我们去吃美食汇,这家餐厅有高大上的气氛。海里游的东西阿姨一直给我们拿,当日吃撑了,非常舒服。
day1考完我没有对答案,L哥晚上看手机惊呼自己太菜了,把我弄的很虚。但是有信仰在,不怕。晚上我跑到隔壁房间跟L神和L巨聊天,L巨考得不错,L神没发挥好。然后我们一起看电视,话说我是多久没有看电视了。看了一个人工智能挑战的娱乐节目、新闻联播、大国工程什么的。然后L哥点了外卖,吃炸鸡也非常开心。Z教练又把我们喊去开会,再次强调了注意事项以及网络流,计算几何。凸包我不会,心里发怵。但是没考。
day2
早上吃了昨晚买的牛奶和面包,仍然坐在寒风刺骨的大厅里等,后悔出来不多穿一点衣服。考室里倒是开了空调挺暖和,热得我脱衣服。
T1
树的情况的dp很好写。环就枚举删上面那条点就行了。时间复杂度\(O(n^2 \log n)\),最后得了88分。莫名其妙,鬼火乱冒,出题人卡常!洛谷上我交是A了的。
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
#define rg register
#define il inline
template<class T>il T read(rg T&x)
{
rg T data=0;
rg int w=1;
rg char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
{
data=data*10+ch-'0';
ch=getchar();
}
return x=data*w;
}
using namespace std;
typedef long long ll;
const int MAXN=5e3+7;
int n,m;
struct edge
{
int nx,fr,to;
}e[MAXN<<1];
int head[MAXN],ecnt;
il void addedge(rg int x,rg int y)
{
e[++ecnt].to=y,e[ecnt].fr=x;
e[ecnt].nx=head[x],head[x]=ecnt;
}
int dfn[MAXN],low[MAXN],clk;
stack<int>S;
int bl[MAXN],sz[MAXN],id;
il void tarjan(rg int x,rg int fa)
{
S.push(x);
dfn[x]=low[x]=++clk;
for(rg int i=head[x];i!=-1;i=e[i].nx)
{
rg int y=e[i].to;
if(y==fa)
continue;
if(!dfn[y])
tarjan(y,x);
low[x]=min(low[x],low[y]);
}
if(low[x]==dfn[x])
{
++id;
while(S.top()!=x)
{
rg int y=S.top();
S.pop();
bl[y]=id;
++sz[id];
}
bl[x]=id;
++sz[id];
S.pop();
}
}
bool mark[MAXN<<1];
vector<int>v;
vector<int>f[MAXN];
il void dfs1(rg int x,rg int fa)
{
f[x].clear();
for(rg int i=head[x];i!=-1;i=e[i].nx)
{
if(mark[i])
continue;
rg int y=e[i].to;
if(y==fa)
continue;
f[x].push_back(y);
dfs1(y,x);
}
sort(f[x].begin(),f[x].end());
}
vector<int>ans,res;
il void dfs2(rg int x)
{
res.push_back(x);
for(rg int i=0;i<f[x].size();++i)
dfs2(f[x][i]);
}
void judge()
{
if(!ans.size())
{
ans=res;
return;
}
for(rg int i=0;i<res.size();++i)
if(ans[i]^res[i])
{
if(ans[i]>res[i])
{
ans=res;
return;
}
else
return;
}
}
int main()
{
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
read(n);read(m);
fill(head+1,head+n+1,-1);
ecnt=-1;
for(rg int i=1;i<=m;++i)
{
rg int x,y;
read(x);read(y);
addedge(x,y);
addedge(y,x);
}
tarjan(1,0);
rg int circle=0;
for(rg int i=1;i<=id;++i)
if(sz[i]>1)
{
circle=i;
break;
}
// cerr<<"circle="<<circle<<endl;
// cerr<<"bl="<<endl;
// for(int i=1;i<=n;++i)
// cerr<<i<<" bl="<<bl[i]<<endl;
if(!circle)
{
dfs1(1,0);
dfs2(1);
ans=res;
}
else
{
for(rg int i=0;i<=ecnt;i+=2)
if(bl[e[i].fr]==circle&&bl[e[i].to]==circle)
{
v.push_back(i);
// cerr<<e[i].fr<<" with "<<e[i].to<<" in circle"<<endl;
}
for(rg int i=0;i<v.size();++i)
{
mark[v[i]]=1;
mark[v[i]^1]=1;
dfs1(1,0);
res.clear();
dfs2(1);
// cerr<<"res="<<endl;
// for(int j=0;j<res.size();++j)
// cerr<<res[j]<<" ";
// cerr<<endl;
judge();
mark[v[i]]=0;
mark[v[i]^1]=0;
}
}
for(rg int i=0;i<ans.size();++i)
printf("%d ",ans[i]);
return 0;
}
T2
开始回顾血泪史。
想了半天dp,的确是半天,貌似是对的,但是写不出来。但在考场上我执意要把这题A了,我太贪了,导致最后只好打了个骗分,连20分我都没有把握,之后已经没有时间做其他题了。最后得了15分。但在洛谷上我有40分?
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<algorithm>
template<class T>T read(T&x)
{
T data=0;
int w=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
{
data=data*10+ch-'0';
ch=getchar();
}
return x=data*w;
}
using namespace std;
typedef long long ll;
const int MAXN=1e6+9,mod=1e9+7;
int n,m,mx;
int f[MAXN][9][9];
int calc(int x)
{
if(n<=x&&x<=m)
return n;
else if(x<n)
return x;
else if(x>m)
return n+m-x;
}
int add(int x,int y)
{
x+=y;
return x>=mod?x-mod:x;
}
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
read(n);read(m);
if(n==3&&m==3)
{
puts("112");
return 0;
}
mx=(m+n-1);
int s1=mx-1,s2=mx;
int c1=calc(s1),c2=calc(s2);
for(int i=0;i<=c1;++i)
for(int j=0;j<=c2;++j)
{
f[s1][i][j]=1;
// cerr<<"f "<<s1<<" "<<i<<" "<<j<<" = "<<1<<endl;
}
vector<int>v1,v2;
for(s1=mx-3,s2=mx-2;s2>=1;s1-=2,s2-=2)
{
c1=calc(s1),c2=calc(s2);
for(int i=0;i<=c1;++i)
for(int j=0;j<=c2;++j)
{
// cerr<<"cal "<<s1<<" "<<i<<" "<<j<<" c="<<c1<<" "<<c2<<endl;
v1.clear();
v2.clear();
v1.push_back(0);
v1.push_back(n-c1+i-1);
for(int k=c1-1;k<=n;++k)
v1.push_back(k);
sort(v1.begin(),v1.end());
v1.erase(unique(v1.begin(),v1.end()),v1.end());
// cerr<<"v1="<<endl;
// for(int k=0;k<v1.size();++k)
// cerr<<v1[k]<<" ";
// cerr<<endl;
v2.push_back(0);
v2.push_back(n-c2+j-1);
for(int k=c2-1;k<=n;++k)
v2.push_back(k);
sort(v2.begin(),v2.end());
v2.erase(unique(v2.begin(),v2.end()),v2.end());
// cerr<<"v2="<<endl;
// for(int k=0;k<v1.size();++k)
// cerr<<v2[k]<<" ";
// cerr<<endl;
for(int k=0;k<v1.size();++k)
if(v1[k]>=0)
for(int l=0;l<v2.size();++l)
if(v2[l]>=0)
f[s1][i][j]=add(f[s1][i][j],f[s1+2][v1[k]][v2[l]]);
// cerr<<"f="<<f[s1][i][j]<<endl;
}
}
s1+=2,s2+=2; // edit 1
c1=calc(s1),c2=calc(s2);
int ans=0;
for(int i=0;i<=c1;++i)
for(int j=0;j<=c2;++j)
ans=add(ans,f[s1][i][j]);
printf("%d\n",ans);
return 0;
}
T3
据说是动态dp,但是我连题都没看,爆0了。
成绩
算下来,CCF坑了我37分。但是最重要的是策略问题,我应该把day2T3暴力打了的,这样还有44分。
没上400主要原因还是我太贪了,CCF少爷机卡常也有锅,但那不是最主要的。
最重要的是我太年轻,太菜了!
那就只能屈辱地拿个一等奖退役了。