这次是我第一次打 USACO qaq
于是参加了铜组和银组(铜组 AK 提前晋级)。
USACO 2021 Jan
Bronze
铜组的题都很水啦。。
T1
直接模拟。将 Farmer John 听到的字符串扫一遍,如果有相邻的两个字母在牛版字母表中逆序,就重新开一个新的字母歌。
1 2 3 4 5 6 7 8 9 10 11 12
| rep(i,0,25) { to[s[i]-'a']=i; } int l=strlen(t+1); int ans=1,now=-1; rep(i,1,l) { if(to[t[i]-'a']<=now) { now=to[t[i]-'a']; ++ans; } else now=to[t[i]-'a']; }
|
T2
先统计奇数和偶数的个数。
由于题目要求一奇一偶交替,于是我们可以先把奇数和偶数中个数最少的那个先用完。
然后只剩下一堆奇数或者一堆偶数了。
如果剩下一堆偶数,那么可以把这堆偶数搓成一团放在下一个。答案 +1。
如果剩下一堆奇数,由于题目要保证所有的牛都划分完,所以我们分奇数个数mod3 的三种情况讨论即可。具体见代码:
1 2 3 4 5 6 7 8
| int ans=min(ji,ou); ji-=ans,ou-=ans; if(ou) printf("%d\n",ans*2+1); else { if(ji%3==0) printf("%d\n",ans*2+(ji/3)*2); else if(ji%3==2) printf("%d\n",ans*2+(ji/3)*2+1); else printf("%d\n",ans*2+((ji-2)/3)*2+1); }
|
T3
考虑乘法原理。
我们把牛从高到低考虑,因为如果先考虑低牛的话,低牛可能会占用高牛的必须位置,但是先考虑高牛就不会影响到低牛的方案数。
然后就是裸的乘法原理了。对于每头牛,将答案乘以这头牛的方案数即可。别忘了乘完之后下一头牛的方案数要 −1,因为这头牛占用了下一头牛的一个可能情况(只是可能情况,而不是必须位置)。
1 2 3 4 5 6 7 8 9
| sort(a+1,a+n+1); sort(b+1,b+n+1); int ans=1,now=0; per(i,n,1) { int id=lower_bound(b+1,b+n+1,a[i])-b; id=n-id+1; ans*=id-now; ++now; }
|
Silver
这次银组还是比较有含金量的。
T1
我们先模拟一轮,看每头牛分别能到哪里,对每头牛开一个 set
。
模拟完一轮之后,我们可以找到这个序列中所有的环。
然后将这些环中的 set
合并即可。
一个节省代码量的小 trick:将两个 set
合并可以这么写:
1
| a.insert(b.begin(),b.end());
|
核心代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| void dfs(int u,int k,bool first) { if(!first&&u==k) return ; vis[u]=1; s[k].insert(s[u].begin(),s[u].end()); dfs(cow[u],k,0); } void dfs2(int u,int k,bool first) { if(!first&&u==k) return ; ans[u]=s[k].size(); dfs2(cow[u],k,0); } int main() { scanf("%d%d",&n,&k); rep(i,1,n) cow[i]=i,s[i].insert(i); rep(i,1,k) { int a,b; scanf("%d%d",&a,&b); swap(cow[a],cow[b]); s[cow[b]].insert(b); s[cow[a]].insert(a); } rep(i,1,n) if(!vis[i]) { dfs(i,i,1); dfs2(i,i,1); } rep(i,1,n) printf("%d\n",ans[i]); return 0; }
|
T2
我们可以先 dp 求出前缀的答案和后缀的答案。
具体可以开一个桶,转移的时候分两种情况讨论即可。
然后对一个挖掉的区间 [l,r],答案就是 prel−1+sufr+1。
详见代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| rep(i,1,n) { if(s[i]>s[i-1]) f[i]=f[i-1]+1,vis[s[i]-'A']=1; else { f[i]=f[i-1]+(!vis[s[i]-'A']); per(j,s[i-1]-'A',s[i]-'A'+1) vis[j]=0; vis[s[i]-'A']=1; } } rep(i,0,25) vis[i]=0; per(i,n,1) { if(s[i]>s[i+1]) g[i]=g[i+1]+1,vis[s[i]-'A']=1; else { g[i]=g[i+1]+(!vis[s[i]-'A']); per(j,s[i+1]-'A',s[i]-'A'+1) vis[j]=0; vis[s[i]-'A']=1; } } rep(i,1,q) { int l,r; scanf("%d%d",&l,&r); printf("%d\n",f[l-1]+g[r+1]); }
|
T3
只会一半分的 N≤10 做法qwq
具体来说就是,我们确定了一行一列之后,其它的格子都可以直接确定。
那么爆搜这一行一列即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| void dfs(int x,int y) { int qwq=0; if(x==1&&y==n+1) { rep(i,2,n) rep(j,2,n) { int tmp=now[i-1][j-1]+now[i][j-1]+now[i-1][j]; if(!tmp||tmp==3) {anss-=qwq;return;} else if(tmp==1) anss+=a[i][j],now[i][j]=1,qwq+=a[i][j]; else now[i][j]=0; } ans=max(ans,anss); anss-=qwq; return ; } if(x==n+1&&y==1) x=1,y=2; if(y==1) { now[x][y]=1; anss+=a[x][y]; dfs(x+1,y); now[x][y]=0; anss-=a[x][y]; dfs(x+1,y); } else { now[x][y]=1; anss+=a[x][y]; dfs(x,y+1); now[x][y]=0; anss-=a[x][y]; dfs(x,y+1); } }
int main() { scanf("%d",&n); rep(i,1,n) rep(j,1,n) scanf("%d",&a[i][j]); dfs(1,1); printf("%d\n",ans); return 0; }
|
总结
Bronze 没啥好说的,轻松 AK。
Silver C 没想出来正解,不过估计能上 Gold 了。