USACO2023 Cu,Ag,Au 题解

晚上没事干,于是写了。

Cu:1 h 25 min

Ag:2 h 40 min

Au:2 h 15 min

做最久的竟然是 Ag T1。

Cu

T1

诈骗题,做了 50 min。考虑如果越过了 \(a_i\) 往后走,那么 \(a_i\) 的高度至少翻了一倍。

直接模拟即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=2e5+5;
int n,m,a[N];

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    while(m--)
    {
        int x=1,y;cin>>y;
        for(int i=1;i<=n;i++)
        {
            if(a[i]>=x)
            {
                int t=a[i]+1;
                a[i]+=min(a[i],y)-x+1;
                x=t;
            }
            if(x>y) break;
        }
    }
    for(int i=1;i<=n;i++) cout<<a[i]<<'\n';
}

T2

求出至多可以传染多少天,设为 \(d\),对于每个极长连续段,求出 \(d\) 天传染完最少需要多少奶牛。

#include<bits/stdc++.h>
using namespace std;

const int N=3e5+5;
int n,m,d=1e9,a[N];
char s[N];

int main()
{
    scanf("%d%s",&n,s+1);
    for(int i=1,j;i<=n;i++)
        if(s[i]=='1')
        {
            int j=i;
            while(j+1<=n&&s[j+1]=='1') j++;
            int l=j-i+1;
            a[++m]=l;
            if(i==1||j==n) d=min(d,j-i);
            else
            {
                if(l&1) d=min(d,(l-1)/2);
                else d=min(d,(l-2)/2);
            }
            i=j;
        }
    int ans=0;
    d=d<<1|1;
    for(int i=1;i<=m;i++) ans+=ceil(a[i]*1.0/d);
    cout<<ans<<'\n';
}

T3

可以发现植物的高度可以表示为一次函数。按所有直线的 rank 排序,限制变为直线 \(y_i=k_ix+b_i>y_{i+1}=k_{i+1}x+b_{i+1}\),求交点每次缩小范围即可,注意分类讨论。

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=2e5+5;
int n;
struct node{int k,b,rk;} a[N];

void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i].b;
    for(int i=1;i<=n;i++) cin>>a[i].k;
    for(int i=1;i<=n;i++) cin>>a[i].rk;
    sort(a+1,a+1+n,[&](node a,node b){return a.rk<b.rk;});
    int l=0,r=1e9;
    for(int i=1;i<n;i++)
    {
        auto [k1,b1,r1]=a[i];
        auto [k2,b2,r2]=a[i+1];
        if(k1==k2) {if(b2>=b1) {cout<<-1<<'\n';return;}continue;}
        int x=(b1-b2)/(k2-k1),f=((b1-b2)%(k2-k1)==0);
        if(x<0) continue;
        if(b1>=b2) r=min(r,x-f);
        else l=max(l,x+1);
    }
    if(l>r) cout<<-1<<'\n';
    else cout<<l<<'\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int t;cin>>t;
    while(t--) solve();
}

Ag

T1

贪心。按 \(w_i\) 排序。我们只用考虑当前重 \(w_i\) 的奶牛有多少能叠到之前。单调队列维护。

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=2e5+5;
int n,m,k,ans;
struct cow{int w,c;} a[N];
deque<cow> q;

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++) cin>>a[i].w>>a[i].c;
    sort(a+1,a+1+n,[&](cow a,cow b){return a.w<b.w;});
    q.push_back({-k,m});
    for(int i=1;i<=n;i++)
    {
        auto [w,c]=a[i];
        int _c=c;
        while(q.size()&&q.front().w+k<=w)
        {
            if(q.front().c<c) c-=q.front().c,q.pop_front();
            else {q.front().c-=c;c=0;break;}
        }
        ans+=_c-c;
        q.push_back({w,_c-c});
    }
    cout<<ans<<'\n';
}

T2

只考虑对 \(b\) 左移,然后 reverse 一下再算一遍。考虑让两个值相同需要左移多少,用桶 \(t\) 统计让 \(b\) 数组左移 \(i\) 位能让多少值相同,取个 max 即可。

#include<bits/stdc++.h>
using namespace std;

const int N=5e5+5;
int n,k,ans,a[N],b[N],t[N],pa[N],pb[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>k;
    for(int i=1;i<=k;i++) cin>>a[i],t[a[i]]++,pa[a[i]]=i;
    for(int i=1;i<=k;i++) cin>>b[i],t[b[i]]++,pb[b[i]]=i;
    for(int i=1;i<=n;i++) ans+=!t[i];
    memset(t,0,sizeof(t));int mx=0;
    for(int i=1;i<=n;i++) if(pa[i]&&pb[i]) t[(pa[i]-pb[i]+k)%k]++;
    for(int i=0;i<=k;i++) mx=max(mx,t[i]);
    memset(t,0,sizeof(t));
    for(int i=1;i<=n;i++) if(pb[i]) pb[i]=k-pb[i]+1;
    for(int i=1;i<=n;i++) if(pa[i]&&pb[i]) t[(pa[i]-pb[i]+k)%k]++;
    for(int i=0;i<=k;i++) mx=max(mx,t[i]);
    cout<<ans+mx<<'\n';
}

T3

傻逼细节分讨题。记 所有字符为 F 的位置集合为 \(P\)

那么改变当前字符 \(c_i\) 可能让后面所有 \(j\in P,j>i\) 左移一位,左移两位,右移一位,右移两位。

对着四种情况分别开一个桶,分类讨论维护一下。

#include<bits/stdc++.h>
using namespace std;

const int N=2e5+15;
char s[N];
int n,m,ans,cl1,cl2,cr1,cr2,a[N],t[N],_t[N],p[N],tl1[N],tl2[N],tr1[N],tr2[N];
vector<int> pos;

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]+=m+2,t[a[i]]=1;
    scanf("%s",s+1);
    memcpy(_t,t,sizeof(t));
    p[0]=m+2;
    for(int i=1;i<=m;i++)
    {
        if(s[i]=='L') p[i]=p[i-1]-1;
        if(s[i]=='R') p[i]=p[i-1]+1;
        if(s[i]=='F') p[i]=p[i-1],ans+=t[p[i]],t[p[i]]=0,pos.push_back(p[i]);
    }
    memcpy(t,_t,sizeof(t));
    for(int i:pos)
    {
        if(t[i-1]&&!tl1[i-1]++) cl1++;
        if(t[i-2]&&!tl2[i-2]++) cl2++;
        if(t[i+1]&&!tr1[i+1]++) cr1++;
        if(t[i+2]&&!tr2[i+2]++) cr2++;
    }
    for(int i=1;i<=m;i++)
    {
        int x=p[i];
        if(s[i]=='L') ans=max(ans,cr1+(t[x+1]&&!tr1[x+1])),ans=max(ans,cr2);
        if(s[i]=='R') ans=max(ans,cl1+(t[x-1]&&!tl1[x-1])),ans=max(ans,cl2);
        if(s[i]=='F')
        {
            if(t[x-1]&&!--tl1[x-1]) cl1--;
            if(t[x-2]&&!--tl2[x-2]) cl2--;
            if(t[x+1]&&!--tr1[x+1]) cr1--;
            if(t[x+2]&&!--tr2[x+2]) cr2--;
            ans=max(ans,max(cl1,cr1));
            if(t[x]&&!tl1[x]++) cl1++;
            if(t[x]&&!tl2[x]++) cl2++;
            if(t[x]&&!tr1[x]++) cr1++;
            if(t[x]&&!tr2[x]++) cr2++;
        }
    }
    cout<<ans<<'\n';
}

Au

T1

最简单的 T1

以两个城市的间隔为阶段,进行一个类似 floyd 的过程。

假设当前两个城市为 \(i,j\),先计算出之前 \(i\rightarrow j\) 的路径数奇偶性。

如果不同,那么添加一条道路。

注意可能重复统计路径,我们枚举和 \(i\) 直接连边的点 \(k\),只统计 \(k\rightarrow j\) 的路径数。

#include<bits/stdc++.h>
using namespace std;

const int N=755;
char a[N][N];
int n,ans,f[N][N];
vector<int> p[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            cin>>a[i][j],a[i][j]-='0';
    for(int l=1;l<n;l++)
        for(int i=1,j=i+l;j<=n;i++,j++)
        {
            for(int k:p[i]) f[i][j]^=f[k][j];
            if(f[i][j]!=a[i][j]) f[i][j]=a[i][j],p[i].push_back(j),ans++;
        }
    cout<<ans<<'\n';
}

T2

倍增 + Hash。

先建出反图,然后在 DAG 上拓扑转移。

比较两个相同长度的字符串的字典序用倍增 + 哈希。

然后这题还卡 Hash。建议乱搞哈希值,乱取模数。

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;

const int N=2e5+5,base=114514;
int n,m,d[N],f[N][20],g[N][20],l[N],ans[N];
ull hs[N][20],ht[N][20],pw[N];
vector<pair<int,int>> G[N];

void get(int u,int v,int w)
{
    l[u]=l[v]+1,g[u][0]=v,ht[u][0]=w;
    for(int i=1;i<=17;i++)
    {
        g[u][i]=g[g[u][i-1]][i-1];
        ht[u][i]=w*base+ht[u][i-1]*pw[1<<i-1]+ht[g[u][i-1]][i-1];
    }
}

int dfs(int u)
{
    if(!u) return 0;
    if(ans[u]) return ans[u];
    return ans[u]=dfs(f[u][0])+hs[u][0];
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    pw[0]=1;for(int i=1;i<=n;i++) pw[i]=pw[i-1]*base;
    for(int i=1;i<=m;i++)
    {
        int u,v,l;cin>>u>>v>>l;
        G[v].push_back({u,l}),d[u]++;
    }
    queue<int> q;
    for(int i=1;i<=n;i++) if(!d[i]) q.push(i);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(auto [v,w]:G[u])
        {
            if(l[u]+1>l[v])
            {
                l[v]=l[u]+1;get(v,u,w);
                memcpy(f[v],g[v],sizeof(f[v]));memcpy(hs[v],ht[v],sizeof(hs[v]));
            }
            if(l[u]+1==l[v])
            {
                get(v,u,w);
                int x=v,y=v;
                for(int i=__lg(l[v]);~i;i--)
                    if(f[x][i]&&ht[x][i]==hs[y][i]) x=g[x][i],y=f[y][i];
                if(ht[x][0]<hs[y][0]) memcpy(f[v],g[v],sizeof(f[v])),memcpy(hs[v],ht[v],sizeof(hs[v]));
            }
            if(!--d[v]) q.push(v);
        }
    }
    for(int i=1;i<=n;i++) ans[i]=dfs(i);
    for(int i=1;i<=n;i++) cout<<l[i]<<" "<<ans[i]<<'\n';
}

T3

对于每个位置 \(i\),可以求出一个点对 \((x_i,y_i)\),选当前点的答案为 \(ax_i+by_i\)

可以发现随着 \(i\) 的增大,\(x_i\) 是递增的,\(y_i\) 是递减的。不妨把 \(y_i\) 取反,两个值都递增。

\(ax_i+by_i<ax_j+by_j\)

\(a(x_i-x_j)<b(y_j-y_i)\)

\(\frac{x_i-x_j}{y_j-y_i}<\frac{b}{a}\)

相当于坐标系上有一些点对 \((x_i,y_i)\),用斜率为 \(\frac{b}{a}\) 的直线去切,二分即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=1e6+5;
int n,m,q,a,b,V=1e6,c[N],s[N],v[N];
struct node{int x,y;} p[N];

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1,x;i<=n;i++) cin>>x,s[x]+=x,c[x]++,v[x]=1;
    for(int i=1;i<=V;i++) s[i]+=s[i-1],c[i]+=c[i-1];
    for(int i=0;i<=V;i++)
        if(v[i])
        {
            int a=(i-1>=0?c[i-1]*i-s[i-1]:0),b=s[V]-s[i]-(n-c[i])*i;
            p[++m]={a,-b};
        }
    cin>>q;
    while(q--)
    {
        cin>>a>>b;
        int l=1,r=m,ans=1;
        while(l<r)
        {
            int mid=l+r>>1;
            if(a*(p[mid].x-p[mid-1].x)<b*(p[mid].y-p[mid-1].y)) ans=mid,l=mid+1;
            else r=mid;
        }
        cout<<a*p[ans].x-b*p[ans].y<<'\n';
    }
}
posted @ 2023-12-19 21:26  spider_oyster  阅读(58)  评论(1编辑  收藏  举报