晚上没事干,于是写了。
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';
}
}
标签:USACO2023,const,int,题解,cin,long,Au,tie,include
From: https://www.cnblogs.com/spider-oyster/p/17914799.html