noip2018提高組遊記
upd:
程式碼下發了,重測了民間成績,靜等11.19出官方成績
以及:程式碼全部改為考場程式碼
Day1:
原題大賽,水題大聯歡,服了
希望Day2不是爆零賽
T1:鋪設道路
這差分一下不就是sb題了嗎
複雜度
#include<bits/stdc++.h> #define sz 100010 using namespace std; typedef long long ll; void read(ll &x) { char ch=getchar();x=0; while (ch>'9'||ch<'0') ch=getchar(); while (ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar(); } ll b[sz],a[sz]; int n; int main() { // freopen("road.in","r",stdin);freopen("road.out","w",stdout); ll i,x; cin>>n; for (i=1;i<=n;i++) read(b[i]),b[i]*=-1; for (i=1;i<=n;i++) a[i]=b[i]-b[i-1]; x=0; for (i=1;i<=n;i++) if (a[i]<0) x-=a[i]; cout<<x; return 0; }
T2:貨幣系統
看完題目一臉蒙逼,思考了一下,猜了個結論:新貨幣系統中所有幣值都是原來有的
證明:
先將可以被其他錢幣表示出的幣值刪除,sort一下
最小的幣值一定要取
那麼對於:將它表示出的方法只有選擇的幣值
注意到時選擇該幣值會導致在原來的系統中x無法被表示出來,但現在可以
因此,必選。
同理,必選,證畢
複雜度
#include<bits/stdc++.h> #define sz 111 using namespace std; void read(int &x) { char ch=getchar();x=0; while (ch>'9'||ch<'0') ch=getchar(); while (ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar(); } int a[sz]; int n; bool dp[26000]; int main() { // freopen("money.in","r",stdin);freopen("money.out","w",stdout); int T,i,j; read(T); while (T--) { read(n); memset(a,0,sizeof(a)); int maxv=-1; for (i=1;i<=n;i++) read(a[i]),maxv=max(maxv,a[i]); sort(a+1,a+n+1); int ans=0; memset(dp,0,sizeof(dp)); dp[0]=1; for (i=1;i<=n;i++) { if (!dp[a[i]]) ++ans; else continue; for (j=a[i];j<=maxv;j++) dp[j]|=dp[j-a[i]]; } printf("%d\n",ans); } return 0; }
T3:賽道修建
這題稍微難一些
“最短的賽道最長”,一眼二分答案
對於每個點,經過它的賽道要麼從一棵子樹走到另一棵子樹,要麼往自己的父節點走
由於自己到父節點的賽道只能走一次,那麼優先讓子樹內賽道盡可能多,然後才是延伸上去的道路儘量長
對於每一個點先貪心求出前者最大值,然後二分求出滿足前者最大時後者的最大值
最後統計每個點內賽道總數,看是否大於等於m
複雜度(吧)。用了vector,常數略大,但在洛谷的民間資料跑過了
#include<bits/stdc++.h> #define sz 50050 using namespace std; void read(int &x) { char ch=getchar();x=0; while (ch>'9'||ch<'0') ch=getchar(); while (ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar(); } int n,m; struct hh{int t,w,nxt;}edge[sz<<1]; int head[sz],ecnt; void make_edge(int f,int t,int w) { edge[++ecnt]=(hh){t,w,head[f]}; head[f]=ecnt; edge[++ecnt]=(hh){f,w,head[t]}; head[t]=ecnt; } #define go(x) for (int i=head[x];i;i=edge[i].nxt) #define v edge[i].t int dp[sz]; int ans[sz]; int A; void dfs(int x,int fa) { dp[x]=ans[x]=0; vector<int>ret; go(x) if (v!=fa) { dfs(v,x);dp[v]+=edge[i].w; if (dp[v]<A) ret.push_back(dp[v]); else ans[x]++; } sort(ret.begin(),ret.end()); int _ans=0,cur=0,i; for (i=ret.size()-1;i>=0;i--) { if (i<=cur) break; while (cur<i&&ret[cur]+ret[i]<A) ++cur; if (i<=cur) break; ++_ans;++cur; } ans[x]+=_ans; if (_ans*2==ret.size()) return; int l=0,r=ret.size()-1,_ret; while (l<=r) { int mid=(l+r)>>1; cur=0;int t=0; for (i=ret.size()-1;i>=0;i--) { if (i==mid) --i; if (i<0) break; while (cur<i&&ret[cur]+ret[i]<A) ++cur; if (cur==mid) ++cur; if (cur>=i) break; ++t;++cur; } if (t==_ans) _ret=mid,l=mid+1; else r=mid-1; } dp[x]=ret[_ret]; } bool judge() { dfs(1,0); int ret=0; for (int i=1;i<=n;i++) ret+=ans[i]; return ret>=m; } int main() { // freopen("track.in","r",stdin);freopen("track.out","w",stdout); int i,x,y,z; read(n);read(m); for (i=1;i<n;i++) read(x),read(y),read(z),make_edge(x,y,z); int l=1,r=5e8,_out; while (l<=r) { int mid=(l+r)>>1;A=mid; if (judge()) l=mid+1,_out=mid; else r=mid-1; } cout<<_out; return 0; }
Day1估計得分:100+100+100=300(希望別寫掛)
Day2.RP++,爭取不爆零拿一等
Day2:
真·爆零賽
Day1考好之後我已經頹廢了,今天亂打了一通
T1:旅行
基環樹是啥?這題咋寫???
想了一個碼量超大的做法,但我慫了,只寫了60分的樹+12分的環
#include<bits/stdc++.h>
#define sz 6666
using namespace std;
int n,m;
struct hh{int t,nxt;}edge[sz<<1];
int head[sz],ecnt;
void make_edge(int f,int t)
{
edge[++ecnt]=(hh){t,head[f]};
head[f]=ecnt;
edge[++ecnt]=(hh){f,head[t]};
head[t]=ecnt;
}
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
#define v edge[i].t
int ans[sz],cnt;
void dfs_tree(int x,int fa)
{
vector<int>ans;
::ans[++cnt]=x;
go(x) if (v!=fa) ans.push_back(v);
sort(ans.begin(),ans.end());
for (int i=0;i<ans.size();i++) dfs_tree(ans[i],x);
}
bool vis[sz];
void dfs_circle(int x,bool back,int st)
{
vis[x]=1;ans[++cnt]=x;
int y=-1;
go(x) if (!vis[v]) {y=v;break;}
if (y==-1) return;
if (!back) return dfs_circle(y,0,0);
if (y<st) return dfs_circle(y,1,st);
dfs_circle(st,0,0);
}
int main()
{
// freopen("travel.in","r",stdin);freopen("travel.out","w",stdout);
int i,j,k,x,y,z;
scanf("%d %d",&n,&m);
for (i=1;i<=m;i++)
scanf("%d %d",&x,&y),make_edge(x,y);
if (m==n-1)
{
dfs_tree(1,0);
for (i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}
y=z=0;
go(1)
if (!y) y=v;
else z=v;
ans[cnt=1]=1;vis[1]=1;
if (y<z) dfs_circle(y,1,z);
else dfs_circle(z,1,y);
for (i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}
賽後聽人說是列舉斷開環上的哪條邊,然後暴力在樹上跑,複雜度
我太菜了。。。
T2:填數遊戲
一眼不會做題,先滾去T3搞了一陣再回來
一開始感覺是數學題,推了一陣,發現(3,3)的樣例沒過
檢查了一下,無果,心態爆炸
先寫了個暴力亂dfs,發現(3,3)時答案真的是112
靈光一閃:“這題能不能打表找規律”??
瞪眼法+分解質因數,n<=3時推出一個玄學規律,具體看程式碼
#include<bits/stdc++.h>
#define mod (ll)(1e9+7)
using namespace std;
typedef long long ll;
int n,m;
ll ans;
int mat[666][666];
int t[999][999],cnt;
int cur[999];
void judge(int x,int y)
{
cur[x+y-1]=mat[x][y];
if (x==n&&y==m)
{
++cnt;
for (int i=1;i<n+m;i++) t[cnt][i]=cur[i];
return;
}
if (y!=m) judge(x,y+1);
if (x!=n) judge(x+1,y);
}
bool cmp(int *x,int *y)
{
for (int i=1;i<n+m;i++) if (x[i]!=y[i]) return x[i]<y[i];
return 1;
}
bool judge()
{
cnt=0;
judge(1,1);
for (int i=1;i<cnt;i++) if (!cmp(t[i],t[i+1])) return 0;
return 1;
}
void dfs(int dep)
{
if (dep==n*m+1) return (void)(ans+=judge());
int x=(dep-1)/m+1,y=(dep-1)%m+1;
mat[x][y]=0;
dfs(dep+1);
mat[x][y]=1;
dfs(dep+1);
}
ll ksm(ll x,int y)
{
ll ret;
for (ret=1;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;
return ret;
}
int main()
{
// freopen("game.in","r",stdin);freopen("game.out","w",stdout);
int i,j,k,x,y,z;
cin>>n>>m;
if (n==1)
{
cout<<ksm(2,m)%mod;
return 0;
}
if (n==2)
{
cout<<4ll*ksm(3,m-1)%mod;
return 0;
}
if (n==3)
{
cout<<ksm(3,m-3)*16ll%mod*7ll%mod;
return 0;
}
dfs(1);cout<<ans;return 0;
}
T3:保衛王國
這tm什麼東西???dp???動態dp???
這些特殊條件有個鬼用嗎???WTF??
WA地一聲哭出來,寫個44分滾蛋
#include<bits/stdc++.h>
#define sz 100010
using namespace std;
typedef long long ll;
int n,m;
struct hh{int t,nxt;}edge[sz<<1];
int head[sz],ecnt;
void make_edge(int f,int t)
{
edge[++ecnt]=(hh){t,head[f]};
head[f]=ecnt;
edge[++ecnt]=(hh){f,head[t]};
head[t]=ecnt;
}
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
#define v edge[i].t
ll dp[sz][2];
ll w[sz];
int A,X,B,Y;
void dfs(int x,int fa)
{
dp[x][0]=0;dp[x][1]=w[x];
go(x) if (v!=fa) dfs(v,x);
if (B==x) swap(A,B),swap(X,Y);
if (A==x)
{
dp[x][!X]=233333333333333ll;
if (X==0){
go(x) if (v!=fa) dp[x][0]+=dp[v][1];
}else{
go(x) if (v!=fa) dp[x][1]+=min(dp[v][1],dp[v][0]);
}
return;
}
go(x)
if (v!=fa){
dp[x][1]+=min(dp[v][1],dp[v][0]),
dp[x][0]+=dp[v][1];
}
}
int main()
{
// freopen("defense.in","r",stdin);freopen("defense.out","w",stdout);
int i,x,y,Q;string _;
scanf("%d %d",&n,&Q);cin>>_;
for (i=1;i<=n;i++) scanf("%lld",&w[i]);
for (i=1;i<n;i++) scanf("%d %d",&x,&y),make_edge(x,y);
while (Q--)
{
scanf("%d %d %d %d",&A,&X,&B,&Y);
dfs(1,0);
ll ans;
printf("%lld\n",(((ans=min(dp[1][0],dp[1][1]))>=233333333333333ll)?-1ll:ans));
}
}
Day2估計得分72+65+44=181
總分估計100+100+100+72+65+44=481