NOIP2018游记&题解
Day 0
openday,open了一天.和yx,zyh三杀
晚上到了学军旁边的酒店.
看了一下电视睡觉了.
Day 1
早上8点到了考场,唯一的感觉是冷.
8点15分进了考场.
700+台笔记本.
密码纪念金庸
Fei2Xue@Lian#Tian!
T1 铺设道路
刚开始看题
woc这个\(T1\)我不会啊
没做过积木大赛啊QAQ
数据结构学傻掉了
于是
秒想一个分治线段树
对于一段\([L,R]\),记录一个\(val\)表示已经铺设的量,然后线段树区间最小找到\(min\)和\(min\)的位置,答案加上\(val-min\),然后分治就好了.
时间复杂度\(O(n*log^2n)\)
代码略丑,见谅
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N (100010)
#define LL long long
using namespace std;
struct xds{
int l,r,pos;
}t[N<<3];
int n,a[N];
LL ans;
int min_(int x,int y){
if(a[x]<a[y])return x;else return y;
}
void build(int l,int r,int x){
t[x].l=l,t[x].r=r;
if(l==r){
t[x].pos=l;
return;
}
int mid=(l+r)>>1;
build(l,mid,x*2),build(mid+1,r,x*2+1);
t[x].pos=min_(t[x*2].pos,t[x*2+1].pos);
}
int query(int l,int r,int x){
if(t[x].l==l&&t[x].r==r)return t[x].pos;
int mid=(t[x].l+t[x].r)>>1;
if(r<=mid)return query(l,r,x*2);
else if(l>mid)return query(l,r,x*2+1);
else return min_(query(l,mid,x*2),query(mid+1,r,x*2+1));
}
void work(int l,int r,int v){
if(l>r)return;
if(l==r){
ans+=((LL)a[l]-(LL)v);
return;
}
int mi=query(l,r,1),val=a[mi];
ans+=((LL)val-(LL)v);
work(l,mi-1,val),work(mi+1,r,val);
}
int main(){
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,n,1);
work(1,n,0);
printf("%lld\n",ans);
}
大概\(9:00\)左右写完了\(T1\),过了大样例,开\(T2\).
T2 货币系统
联赛前模拟赛曾出现过网友这个词语.
对于网友这个词感到莫名慌张QAQ
woc这个\(T2\)我不会做啊
仔细分析(观察大样例),发现答案必定是原来货币系统的子集.
所以直接可行性DP就好了(或者说完全背包)
一发过了大样例,和暴力拍上就不管了
时间复杂度\(O(n*a_{max})\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N (110)
#define M (25010)
#define LL long long
using namespace std;
int n,a[N],m,ans,T;
bool vis[M];
void check(int x){
for(int i=x;i<=m;i++)
vis[i]|=vis[i-x];
}
int main(){
freopen("money.in","r",stdin);
freopen("money.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%d",&n),m=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),m=max(m,a[i]);
sort(a+1,a+n+1),ans=0;
memset(vis,0,sizeof(vis)),vis[0]=1;
for(int i=1;i<=n;i++)
if(!vis[a[i]])ans++,check(a[i]);
printf("%d\n",ans);
}
}
T3 赛道修建
看见最大值最小
果断二分
然后在树上贪心就好了
我用的是set,常数较大,得分95.
洛谷上90,开了O2跑得飞快
时间复杂度\(O(n*log^2n)\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<set>
#define N (50010)
#define LL long long
using namespace std;
int n,m,fi[N],ne[N<<1],b[N<<1],c[N<<1],E;
int dis[N],f[N],len[N],a[N],ru[N];
bool used[N];
multiset<int> S;
set<int>::iterator it,it2;
void add(int x,int y,int z){
ne[++E]=fi[x],fi[x]=E,b[E]=y,c[E]=z,ru[y]++;
}
void dfs(int u,int pre){
for(int i=fi[u];i;i=ne[i]){
int v=b[i];
if(v==pre)continue;
dis[v]=dis[u]+c[i];
dfs(v,u);
}
}
void dp(int u,int pre,int lim){
int cnt=0;
for(int i=fi[u];i;i=ne[i]){
int v=b[i];
if(v==pre)continue;
dp(v,u,lim),f[u]+=f[v];
}
for(int i=fi[u];i;i=ne[i]){
int v=b[i];
if(v==pre)continue;
if(len[v]+c[i]>=lim)f[u]++;
else a[++cnt]=len[v]+c[i];
}
if(!cnt)return;
if(cnt==1){
len[u]=a[1];
return;
}
sort(a+1,a+cnt+1);
for(int i=1;i<=cnt;i++)used[i]=0;
//S.clear();
for(int i=1;i<=cnt;i++)
S.insert(a[i]);
for(int i=1;i<=cnt;i++){
it2=S.find(a[i]);
if(it2==S.end())continue;
S.erase(it2);
it=S.lower_bound(lim-a[i]);
if(it!=S.end()){
f[u]++;
S.erase(it);
}
else len[u]=a[i];
}
}
bool check(int lim){
memset(f,0,sizeof(f));
memset(len,0,sizeof(len));
dp(1,0,lim);
return f[1]>=m;
}
void dp1(int u,int pre,int lim){
int cnt=0;
for(int i=fi[u];i;i=ne[i]){
int v=b[i];
if(v==pre)continue;
dp(v,u,lim),f[u]+=f[v];
}
for(int i=fi[u];i;i=ne[i]){
int v=b[i];
if(v==pre)continue;
if(len[v]+c[i]>=lim)f[u]++;
else a[++cnt]=len[v]+c[i];
}
if(!cnt)return;
if(cnt==1){
len[u]=a[1];
return;
}
sort(a+1,a+cnt+1);
for(int i=1;i<=cnt;i++)used[i]=0;
for(int i=1;i<=cnt;i++){
if(used[i])continue;
bool flag=0;
for(int j=1;j<=cnt;j++){
if(used[j]||i==j)continue;
if(a[i]+a[j]>=lim){
used[i]=used[j]=1,f[u]++,flag=1;
break;
}
}
if(!flag)len[u]=a[i];
}
}
bool check1(int lim){
memset(f,0,sizeof(f));
memset(len,0,sizeof(len));
dp1(1,0,lim);
return f[1]>=m;
}
int main(){
freopen("track.in","r",stdin);
freopen("track.out","w",stdout);
int L=0,R=0; bool flag=1;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z),add(y,x,z),R+=z;
}
for(int i=1;i<=n;i++)if(ru[i]>5)flag=0;
if(n<=3000)flag=1;
dfs(1,0);
if(m==1){
int ans=0,ll=1;
for(int i=2;i<=n;i++)
if(dis[i]>dis[ll])ll=i;
dis[ll]=0,dfs(ll,0);
for(int i=1;i<=n;i++)
if(dis[i]>ans)ans=dis[i];
printf("%d\n",ans);
return 0;
}
else if(flag){
int ans=0;
while(L<=R){
int mid=(L+R)>>1;
if(check1(mid))L=mid+1,ans=mid;
else R=mid-1;
}
printf("%d\n",ans);
}
else {
int ans=0;
while(L<=R){
int mid=(L+R)>>1;
if(check(mid))L=mid+1,ans=mid;
else R=mid-1;
}
printf("%d\n",ans);
}
}
Day1.5
从考场中出来,大家都\(AK\)了QAQ
我这个\(T3\)菊花图要\(1.5s\)的人感到很慌张
下午打了一会膈膜,休息了一下.
看见个个地方都在喷原题
\(NOIP=POI*N\)
\(T1\)自己抄自己
\(T2,T3=POI\)
Day 2
7:50到了学军.
还是8:15开门
今天的密码是XIAOshushenXIA
特殊字符不记得了.
纪念查大侠
T1 旅行
发现了这是一个\(dfs\)序的问题.
因此先拓扑排序
然后枚举一条边断掉
就好了?
时间复杂度\(O(n^2*logn)\)
环不大,应该速度还行.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N (5010)
#define LL long long
#define inf (0x7f7f7f7f)
using namespace std;
int n,m,fi[N],ne[N<<1],b[N<<1],E;
int c[N][N],ind,a[N],ans[N],q[N],ru[N],pp[N],ck[N],cir[N],sum;
bool vis[N];
void add(int x,int y){
ne[++E]=fi[x],fi[x]=E,b[E]=y,ru[y]++;
}
void dfs(int u,int pre){
int cnt=0; ans[++ind]=u;
for(int i=fi[u];i;i=ne[i]){
int v=b[i];
if(v==pre)continue;
c[u][++cnt]=v;
}
sort(c[u]+1,c[u]+cnt+1);
for(int i=1;i<=cnt;i++)
dfs(c[u][i],u);
}
void dfs1(int u,int pre){
//cout<<u<<" "<<sum<<endl;
cir[++sum]=u,vis[u]=1;
for(int i=fi[u];i;i=ne[i]){
int v=b[i];
if(vis[v]||v==pre)continue;
dfs1(v,u);
}
}
void check(int u,int pre,int L,int R){
int cnt=0; ck[++ind]=u;
//cout<<u<<" "<<L<<" "<<R<<endl;
for(int i=fi[u];i;i=ne[i]){
int v=b[i];
if(v==pre)continue;
if(v==L&&u==R||v==R&&u==L)continue;
c[u][++cnt]=v;
}
sort(c[u]+1,c[u]+cnt+1);
for(int i=1;i<=cnt;i++)
check(c[u][i],u,L,R);
}
bool min_(){
for(int i=1;i<=n;i++)
if(ck[i]<ans[i])return 1;
else if(ck[i]>ans[i])return 0;
return 0;
}
void topsort(){
int h=0,t=0;
for(int i=1;i<=n;i++)
if(ru[i]==1)q[++t]=i,vis[i]=1;
while(h<t){
int u=q[++h];
for(int i=fi[u];i;i=ne[i]){
int v=b[i];
if(vis[v])continue;
ru[v]--;
if(ru[v]<=1&&!vis[v])q[++t]=v,vis[v]=1;
}
}
for(int i=1;i<=n;i++)
if(!vis[i]){
dfs1(i,0);
//cout<<sum<<endl;
break;
}
//for(int i=1;i<=sum;i++)
//cout<<i<<" "<<cir[i]<<" ";
//return;
memset(ans,inf,sizeof(ans));
for(int i=1;i<sum;i++){
ind=0; //cout<<cir[i]<<" "<<cir[i+1]<<endl;
check(1,0,cir[i],cir[i+1]);
if(min_()){
for(int j=1;j<=n;j++)ans[j]=ck[j];
}
}
check(1,0,cir[sum],cir[1]);
if(min_())
for(int j=1;j<=n;j++)ans[j]=ck[j];
}
int main(){
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
if(m==n-1){
dfs(1,0);
for(int i=1;i<=n;i++)printf("%d ",ans[i]);
return 0;
}
topsort();
for(int i=1;i<=n;i++)printf("%d ",ans[i]);
return 0;
}
然后就去肝\(T2,T3\),然而我并不会其中任意一道.
因此\(rush\)了两个暴力,然后去推\(T2\)了.
推了一个\(65\)分的,结果不知道为什么挂了.
然后\(11:40\)的时候发现\(T3\)暴力挂了.
然后赶紧改了一下.
好像\(inf\)开小了,最后才\(40\)分.
T2 填数游戏
至今还是不会(逃
不贴代码了
T3 保卫王国
机房巨魔txc几天前刚学会动态dp
今天就考到了板子题.
然而并不会动态dp,也不会倍增.
于是考后学习了一个动态dp
考虑最小代价=驻军费用和-不驻军城市费用和
因此要使不驻军城市费用和最大
并且不驻军城市是树上的一个独立集
强制选或不选可以用把权值赋为\(\pm\inf\)来实现
于是剩下的就是这道题了
代码如下(略长,见谅)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N (100010)
#define M (200010)
#define inf 1e11
#define rg register int
#define Label puts("NAIVE")
typedef long double ld;
typedef long long LL;
typedef unsigned long long ull;
using namespace std;
inline char read(){
static const int IN_LEN=1000000;
static char buf[IN_LEN],*s,*t;
return (s==t?t=(s=buf)+fread(buf,1,IN_LEN,stdin),(s==t?-1:*s++):*s++);
}
template<class T>
inline void read(T &x){
static bool iosig;
static char c;
for(iosig=false,c=read();!isdigit(c);c=read()){
if(c=='-')iosig=true;
if(c==-1)return;
}
for(x=0;isdigit(c);c=read())x=((x+(x<<2))<<1)+(c^'0');
if(iosig)x=-x;
}
inline char readchar(){
static char c;
for(c=read();!isalpha(c);c=read())
if(c==-1)return 0;
return c;
}
const int OUT_LEN = 10000000;
char obuf[OUT_LEN],*ooh=obuf;
inline void print(char c) {
if(ooh==obuf+OUT_LEN)fwrite(obuf,1,OUT_LEN,stdout),ooh=obuf;
*ooh++=c;
}
template<class T>
inline void print(T x){
static int buf[30],cnt;
if(x==0)print('0');
else{
if(x<0)print('-'),x=-x;
for(cnt=0;x;x/=10)buf[++cnt]=x%10+48;
while(cnt)print((char)buf[cnt--]);
}
}
inline void flush(){fwrite(obuf,1,ooh-obuf,stdout);}
#define int LL
struct Matrix{
LL a[2][2];
Matrix(){memset(a,0,sizeof(a));}
Matrix operator *(Matrix x){
Matrix res;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
res.a[i][j]=max(res.a[i][j],a[i][k]+x.a[k][j]);
return res;
}
}a[N<<4],val[N],ans;
int n,m,kkk;
int fi[M],ne[M],b[M],E,ind;
int top[N],fa[N],siz[N],son[N],dfn[N],rdfn[N],ed[N];
LL f[N][2],all,w[N];
void add(int x,int y){
ne[++E]=fi[x],fi[x]=E,b[E]=y;
}
void dfs1(int u,int pre){
int maxsiz=-1,ma=0; fa[u]=pre;
for(int i=fi[u];i;i=ne[i]){
int v=b[i];
if(v==pre)continue;
dfs1(v,u);
if(siz[v]>maxsiz)maxsiz=siz[v],ma=v;
siz[u]+=siz[v];
}
son[u]=ma,siz[u]++;
}
void dfs2(int u){
dfn[u]=++ind,rdfn[ind]=u;
if(!son[u]){ed[u]=u;return;}
top[son[u]]=top[u],dfs2(son[u]),ed[u]=ed[son[u]];
for(int i=fi[u];i;i=ne[i]){
int v=b[i];
if(v==son[u]||v==fa[u])continue;
top[v]=v,dfs2(v);
}
}
void dp(int u){
for(int i=fi[u];i;i=ne[i]){
int v=b[i];
if(v==fa[u])continue;
dp(v),f[u][0]+=max(f[v][0],f[v][1]);
f[u][1]+=f[v][0];
}
f[u][1]+=1ll*w[u];
}
void build(int l,int r,int x){
if(l==r){
int u=rdfn[l],g0=0,g1=w[u];
for(int i=fi[u];i;i=ne[i]){
int v=b[i];
if(v==fa[u]||v==son[u])continue;
g0+=max(f[v][0],f[v][1]),g1+=f[v][0];
}
a[x].a[0][0]=a[x].a[0][1]=g0;
a[x].a[1][0]=g1,a[x].a[1][1]=-inf;
val[l]=a[x];
return;
}
int mid=(l+r)>>1;
build(l,mid,x*2),build(mid+1,r,x*2+1);
a[x]=a[x*2]*a[x*2+1];
}
void change(int k,int l,int r,int x){
if(l==r){a[x]=val[l];return;}
int mid=(l+r)>>1;
if(k<=mid)change(k,l,mid,x*2);
else change(k,mid+1,r,x*2+1);
a[x]=a[x*2]*a[x*2+1];
}
Matrix query(int l,int r,int L,int R,int x){
if(l==L&&r==R)return a[x];
int mid=(L+R)>>1;
if(r<=mid)return query(l,r,L,mid,x*2);
else if(l>mid)return query(l,r,mid+1,R,x*2+1);
else return query(l,mid,L,mid,x*2)*query(mid+1,r,mid+1,R,x*2+1);
}
void update(int u,LL t){
int pos=dfn[u];
val[pos].a[1][0]+=t-w[u],w[u]=t;
Matrix pre,now;
while(u){
pre=query(dfn[top[u]],dfn[ed[u]],1,n,1);
change(pos,1,n,1);
now=query(dfn[top[u]],dfn[ed[u]],1,n,1);
u=fa[top[u]],pos=dfn[u];
val[pos].a[0][0]+=max(now.a[0][0],now.a[1][0])-max(pre.a[0][0],pre.a[1][0]);
val[pos].a[0][1]=val[pos].a[0][0];
val[pos].a[1][0]+=now.a[0][0]-pre.a[0][0];
}
}
signed main(){
freopen("defense.in","r",stdin);
freopen("defense.out","w",stdout);
read(n),read(m),read(kkk);
for(int i=1;i<=n;i++)read(w[i]),all+=1ll*w[i];
for(int i=1,x,y;i<n;i++){
read(x),read(y);
add(x,y),add(y,x);
}
dfs1(1,0),top[1]=1,dfs2(1),fa[1]=0;
dp(1),build(1,n,1);
while(m--){
int x,y,tmp1,tmp2,op1,op2;
read(x),read(op1),read(y),read(op2);
if(fa[x]==y||fa[y]==x){
if((!op1)&&(!op2)){
puts("-1");
continue;
}
}
tmp1=w[x],tmp2=w[y];
if(op1)update(x,-inf);else update(x,inf);
if(op2)update(y,-inf);else update(y,inf);
ans=query(1,dfn[ed[1]],1,n,1);
LL tmp=max(ans.a[0][0],ans.a[1][0]);
if(!op1)(tmp-=inf)+=tmp1;
if(!op2)(tmp-=inf)+=tmp2;
update(x,tmp1),update(y,tmp2);
printf("%lld\n",all-tmp);
}
}
总结
\(100+100+95+100+50+40=485\)
连暴力都没打满
垫底了
技不如人甘拜下风
省选比别人低\(100\)分怎么打啊...
希望学科营不要像\(NOIP\)一样啊..