1. 程式人生 > >noip2018提高組遊記

noip2018提高組遊記

upd:

程式碼下發了,重測了民間成績,靜等11.19出官方成績

以及:程式碼全部改為考場程式碼

Day1:

原題大賽,水題大聯歡,服了

希望Day2不是爆零賽

T1:鋪設道路

這差分一下不就是sb題了嗎

複雜度O(n)

#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一下

最小的幣值a_1一定要取

那麼對於a_2:將它表示出的方法只有選擇x=a_2-k\cdot a_1,k\in Z的幣值

注意到k\neq 0時選擇該幣值x=a_2-k\cdot a_1,k\in Z^+會導致在原來的系統中x無法被表示出來,但現在可以

因此k=0a_2必選。

同理,a_3,a_4...必選,證畢

複雜度O(T\cdot n\cdot max_{a_i})

#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

複雜度O(nlog^2n)(吧)。用了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;
}

賽後聽人說是列舉斷開環上的哪條邊,然後暴力在樹上跑,複雜度O(nm)

我太菜了。。。

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地一聲哭出來,寫個O(nm)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