隐藏页面特效

The 2021 Sichuan Provincial Collegiate Programming Contest

1|0Preface


下下周还要去打重庆市赛,最近就找些省赛来练练手

不得不说省赛的签到题是真的多,人均10+的过题看起来还是很恐怖的

这场虽然因为徐神缺席,而且前面的题目都让祁神来写导致罚时略高,但无所谓反正最后也摸到了11题(主要是没有字符串)


2|0A. Chuanpai


某不知题意的签到

#include<bits/stdc++.h> int ans[13]={0, 0, 1, 1, 2, 2, 3, 3, 3, 2, 2, 1, 1}; int main(){ int t; scanf("%d", &t); while (t--){ int n; scanf("%d", &n); if (n<=12) printf("%d\n", ans[n]); else printf("0\n"); } return 0; }

3|0B. Hotpot


注意到经过2n轮后锅一定是空的,因此可以先模拟一遍2n轮的情况,再模拟剩下的情况

#include<bits/stdc++.h> using namespace std; const int N = 1e5+5; int t, n, k, m, A[N], ans[N]; bool vis[N]; signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin >> t; while (t--){ cin >> n >> k >> m; for (int i=0; i<n; ++i) cin >> A[i]; for (int i=0; i<n; ++i) ans[i]=0; for (int i=1; i<=k; ++i) vis[i]=false; for (int i=0; i<2*n; ++i){ if (!vis[A[i%n]]) vis[A[i%n]]=true; else vis[A[i%n]]=false, ++ans[i%n]; } for (int i=0; i<n; ++i) ans[i]*=(m/(2*n)); for (int i=0; i<m%(2*n); ++i){ if (!vis[A[i%n]]) vis[A[i%n]]=true; else vis[A[i%n]]=false, ++ans[i%n]; } for (int i=0; i<n; ++i) cout << ans[i] << (i==n-1 ? '\n' : ' '); } return 0; }

4|0C. Triangle Pendant


刚体力学,启动

祁神比赛时候推出了三根绳子都绷直的情况,结果后面发现还有一根/两根绳子绷直的情况,直接寄


5|0D. Rock Paper Scissors


不难发现后手的策略一定是,当知道了先手出的牌后,优先出能赢的牌,若没有就出打平的牌,否则只能输了

而先手的策略一定是选择出某种牌之后直接把这种牌出完,因此可以全排列枚举先手的策略然后模拟算一遍即可

#include<bits/stdc++.h> using namespace std; #define int long long signed main(){ int t; scanf("%lld", &t); while (t--){ int b[3], d[3], tmp1[3], tmp2[3]; for (int i=0; i<3; ++i) scanf("%lld", &b[i]); for (int i=0; i<3; ++i) scanf("%lld", &d[i]); int ans=1e18; for (int i=0; i<3; ++i) for (int j=0; j<3; ++j) for (int k=0; k<3; ++k){ if (i==j || i==k || j==k) continue; int tans=0; memcpy(tmp1, b, sizeof(b)); memcpy(tmp2, d, sizeof(d)); int cur=(i+1)%3; while (tmp1[i]>0){ int res=min(tmp1[i], tmp2[cur]); tmp1[i]-=res; tmp2[cur]-=res; if (cur==(i+1)%3) tans+=res; if (cur==(i-1+3)%3) tans-=res; --cur; if (cur<0) cur+=3; } // printf("i=%d tans=%d\n", i, tans); cur=(j+1)%3; while (tmp1[j]>0){ int res=min(tmp1[j], tmp2[cur]); tmp1[j]-=res; tmp2[cur]-=res; if (cur==(j+1)%3) tans+=res; if (cur==(j-1+3)%3) tans-=res; --cur; if (cur<0) cur+=3; } // printf("j=%d tans=%d\n", j, tans); cur=(k+1)%3; while (tmp1[k]>0){ int res=min(tmp1[k], tmp2[cur]); tmp1[k]-=res; tmp2[cur]-=res; if (cur==(k+1)%3) tans+=res; if (cur==(k-1+3)%3) tans-=res; --cur; if (cur<0) cur+=3; } // printf("k=%d tans=%d\n", k, tans); ans = min(ans, tans); } printf("%lld\n", ans); } return 0; }

6|0E. Don’t Really Like How The Story Ends


寄了一个小时才发现没判重边,我还以为做法假了

不难发现可以贪心构造,考虑已经处理了前i个点,现在要尝试把i+1加进来

按照DFS序找到最靠近栈顶,且还有未访问邻居的点x,若xi+1有边则直接加入i+1,否则必须新加入一条xi+1的边

不难发现用这种方法构造出的答案一定是最小的

#include<bits/stdc++.h> using namespace std; const int N = 1e5+5; int t, n, m; set<int> st[N]; int stk[N], top=-1; int deg[N]; signed main(){ //freopen("1.in", "r", stdin); ios::sync_with_stdio(0); cin.tie(0); cin >> t; while (t--){ cin >> n >> m; for (int i=1; i<=n; ++i) st[i].clear(); memset(deg, 0, (n+1)*sizeof(int)); for (int i=1; i<=m; ++i){ int u, v; cin >> u >> v; if (u==v) continue; if (st[u].count(v)) continue; st[u].insert(v); st[v].insert(u); ++deg[u]; ++deg[v]; } int ans=0; top=-1; stk[++top]=1; for (int i=2; i<=n; ++i){ while (top>0 && deg[stk[top]]<=0) --top; if (0==st[stk[top]].count(i)){ ++ans; // printf("u=%d v=%d\n", stk[top], i); } for (int v : st[i]){ if (v<i) --deg[v], --deg[i]; } stk[++top]=i; } cout << ans << '\n'; } return 0; }

7|0F. Direction Setting


一眼网络流傻逼题

考虑把边看成左边的一排点,原图中的点看作右边的一排点

对于左边的点,从源点向其连容量为1的边;而右边的点向汇点连容量为ai的边

对于每条边的两个端点xi,yi,将其向右边对应的点各连容量为1的边即可

最后的答案就是m减去最大流,方案也很好求

#include<cstdio> #include<iostream> #include<cstring> #include<queue> #define RI register int #define CI const int& using namespace std; const int N=305; int T,n,m,x[N],y[N]; namespace Network_Flow { const int NN=1005,MM=1e6+5,INF=1e9; struct edge { int to,nxt,v; }e[MM<<1]; int cnt=1,head[NN],cur[NN],dep[NN],s,t; queue <int> q; inline void clear(void) { for (RI i=1;i<=t;++i) head[i]=0; cnt=1; } inline void addedge(CI x,CI y,CI z) { e[++cnt]=(edge){y,head[x],z}; head[x]=cnt; e[++cnt]=(edge){x,head[y],0}; head[y]=cnt; } #define to e[i].to inline bool BFS(void) { memset(dep,0,t+1<<2); dep[s]=1; q=queue <int>(); q.push(s); while (!q.empty()) { int now=q.front(); q.pop(); for (RI i=head[now];i;i=e[i].nxt) if (e[i].v&&!dep[to]) dep[to]=dep[now]+1,q.push(to); } return dep[t]; } inline int DFS(CI now,CI tar,int dis) { if (now==tar) return dis; int ret=0; for (RI& i=cur[now];i&&dis;i=e[i].nxt) if (e[i].v&&dep[to]==dep[now]+1) { int dist=DFS(to,tar,min(dis,e[i].v)); if (!dist) dep[to]=INF; dis-=dist; ret+=dist; e[i].v-=dist; e[i^1].v+=dist; if (!dis) return ret; } if (!ret) dep[now]=0; return ret; } #undef to inline int Dinic(int ret=0) { while (BFS()) memcpy(cur,head,t+1<<2),ret+=DFS(s,t,INF); return ret; } inline void solve(void) { for (RI i=1,j;i<=m;++i) { bool flag=0; for (j=head[i];j;j=e[j].nxt) if (e[j].to!=s&&!e[j].v) putchar(e[j].to-m==x[i]?'1':'0'),flag=1; if (!flag) putchar('0'); } putchar('\n'); } }; using namespace Network_Flow; int main() { //freopen("F.in","r",stdin); freopen("F.out","w",stdout); for (scanf("%d",&T);T;--T) { RI i; int a; for (scanf("%d%d",&n,&m),s=n+m+1,t=s+1,i=1;i<=n;++i) scanf("%d",&a),addedge(m+i,t,a); for (i=1;i<=m;++i) scanf("%d%d",&x[i],&y[i]),addedge(s,i,1),addedge(i,m+x[i],1),addedge(i,m+y[i],1); printf("%d\n",m-Dinic()); solve(); clear(); } return 0; }

8|0G. Hourly Coding Problem


说实话没太看懂题解在讲什么,到vjudge上找了各代码一看300行直接不想补了


9|0H. Nihongo wa Muzukashii Desu


签到题,但是有人自作聪明忘了行く的特殊变形我不说是谁(样例没过也能交是真高手)

#include<cstdio> #include<iostream> #include<string> #include<algorithm> #define RI register int #define CI const int& using namespace std; int n; int main() { //freopen("H.in","r",stdin); freopen("H.out","w",stdout); ios::sync_with_stdio(0); cin.tie(0); for (cin>>n;n;--n) { string s; cin>>s; if (s=="ikimasu") { cout<<"itte"<<endl; continue; } reverse(s.begin(),s.end()); if (s.size()>=7&&s[5]=='h'&&s[6]=='s') { string t=s.substr(7); reverse(t.begin(),t.end()); t+="shite"; cout<<t<<endl; } else if (s.size()>=6&&s[5]=='g') { string t=s.substr(6); reverse(t.begin(),t.end()); t+="ide"; cout<<t<<endl; } else if (s.size()>=6&&s[5]=='k') { string t=s.substr(6); reverse(t.begin(),t.end()); t+="ite"; cout<<t<<endl; } else if (s.size()>=6&&s[5]=='m'||s[5]=='b'||s[5]=='n') { string t=s.substr(6); reverse(t.begin(),t.end()); t+="nde"; cout<<t<<endl; } else if (s.size()>=7&&s[5]=='h'&&s[6]=='c') { string t=s.substr(7); reverse(t.begin(),t.end()); t+="tte"; cout<<t<<endl; } else if (s.size()>=6&&s[5]=='r') { string t=s.substr(6); reverse(t.begin(),t.end()); t+="tte"; cout<<t<<endl; } } return 0; }

10|0I. Monster Hunter


挺有意思的一个题,猜了猜结论也艹过去了

首先一眼想到二分答案,这样check的话就只用考虑用已知数量的1,2,3来干掉所有怪兽了

考虑如果只有1,2的话就是各很典的贪心,先尽量用2,如果所有数都被干到1后还有剩下的2就接着干,然后再用剩下的1

而加入3后我们考虑先把3用完,然后套上面的贪心,经过一些手玩我们可以发现:

  • 当某个怪物的血量为大于等于3的奇数时,可以用一个3去干它
  • 当某个怪物的血量为大于等于6的偶数时,可以用两个3去干它

但要注意我们要尽量多的留下偶数,因为这样可以尽量优化2的使用,因此要优先处理第一种情况再考虑第二种情况

然后如果还有多余的3,直接优先干剩余的较大的数即可,然后再套上面的贪心即可

总复杂度O(nlog(m×ai))

#include<cstdio> #include<iostream> #define int long long #define RI register int #define CI const int& using namespace std; const int N=100005; int t,n,a[N],m,h[N],c[5]; inline bool check(CI lim) { RI i; static int tc[5],th[N]; for (i=1;i<=3;++i) tc[i]=c[i]*(lim/n); for (i=1;i<=lim%n;++i) ++tc[a[i]]; for (i=1;i<=m;++i) th[i]=h[i]; for (i=1;i<=m&&tc[3]>0;++i) if (th[i]>=3&&th[i]%2==1) --tc[3],th[i]-=3; for (i=1;i<=m&&tc[3]>0;++i) if (th[i]>=6&&th[i]%2==0) { int dlt=min(tc[3]/2,th[i]/6); tc[3]-=dlt*2; th[i]-=dlt*6; } if (tc[3]==1) { int pos=0; for (i=1;i<=m;++i) if (th[i]>th[pos]) pos=i; --tc[3]; th[pos]-=3; } for (i=1;i<=m&&tc[3]>0;++i) if (th[i]==4) --tc[3],th[i]-=3; for (i=1;i<=m&&tc[3]>0;++i) if (th[i]==2) --tc[3],th[i]=0; for (i=1;i<=m&&tc[3]>0;++i) if (th[i]==1) --tc[3],th[i]=0; for (i=1;i<=m&&tc[2]>0;++i) if (th[i]>0) { int dlt=min(tc[2],th[i]/2); tc[2]-=dlt; th[i]-=dlt*2; } for (i=1;i<=m&&tc[2]>0;++i) if (th[i]==1) --tc[2],th[i]=0; for (i=1;i<=m&&tc[1]>0;++i) { int dlt=min(tc[1],th[i]); tc[1]-=dlt; th[i]-=dlt; } for (i=1;i<=m;++i) if (th[i]>0) return 0; return 1; } signed main() { //freopen("I.in","r",stdin); freopen("I.out","w",stdout); for (scanf("%lld",&t);t;--t) { RI i; for (i=1;i<=3;++i) c[i]=0; for (scanf("%lld",&n),i=1;i<=n;++i) scanf("%lld",&a[i]),++c[a[i]]; for (scanf("%lld",&m),i=1;i<=m;++i) scanf("%lld",&h[i]); int l=1,r=1e14,mid,ret=0; while (l<=r) if (check(mid=l+r>>1)) ret=mid,r=mid-1; else l=mid+1; printf("%lld\n",ret); } return 0; }

11|0J. Ants


我对这题题意一知半解,但据说就是个模拟题,不过实现细节比较考验人需要仔细思考一下

two pointers或队列实现的话可能比较简单

#include<bits/stdc++.h> using namespace std; #define int long long const int N = 1e6+5; const int LEN = 1e9+1; int A[N], d[N]; struct Node{ int id, pos; bool operator<(const Node &b){return pos<b.pos;} }; vector<Node> vL, vR; bool vis[N]; signed main(){ ios::sync_with_stdio(0); cin.tie(0); int n, L, R; cin >> n >> L >> R; for (int i=1; i<=n; ++i) cin >> A[i]; for (int i=1; i<=n; ++i) cin >> d[i]; for (int i=1; i<=n; ++i){ if (0==d[i]){ vL.push_back(Node{i, A[i]}), vR.push_back(Node{i, LEN+A[i]}); vL.push_back(Node{i, 2*LEN+A[i]}), vR.push_back(Node{i, 2*LEN+LEN+A[i]}); }else{ vL.push_back(Node{i, 2*LEN-A[i]}), vR.push_back(Node{i, LEN-A[i]}); vL.push_back(Node{i, 2*LEN+2*LEN-A[i]}), vR.push_back(Node{i, 2*LEN+LEN-A[i]}); } } sort(vL.begin(), vL.end()); sort(vR.begin(), vR.end()); int mn=min(L, R); int ans=0; if (mn>=n) ans+=2*LEN*(mn/n), L-=mn/n*n, R-=mn/n*n; int pL=0, pR=0; int mx=0; while (pL<vL.size() && pR<vR.size()){ if (vL[pL].pos<=vR[pR].pos){ if (!vis[vL[pL].id]){ if (L>0) --L; else mx=max(mx, vL[pL].pos), vis[vL[pL].id]=true; } ++pL; }else{ if (!vis[vR[pR].id]){ if (R>0) --R; else mx=max(mx, vR[pR].pos), vis[vR[pR].id]=true; } ++pR; } } while (pL<vL.size()){ if (!vis[vL[pL].id]){ if (L>0) --L; else mx=max(mx, vL[pL].pos), vis[vL[pL].id]=true; } ++pL; } while (pR<vR.size()){ if (!vis[vR[pR].id]){ if (R>0) --R; else mx=max(mx, vR[pR].pos), vis[vR[pR].id]=true; } ++pR; } cout << ans+mx << '\n'; return 0; }

12|0K. K-skip Permutation


签到题,把modk相同的数升序排在一起即可

#include<bits/stdc++.h> using namespace std; signed main(){ int n, k; scanf("%d%d", &n, &k); vector<int> vec; for (int i=1; i<=k; ++i){ for (int j=0; j*k+i<=n; ++j) vec.push_back(j*k+i); } for (int i=0; i<n; ++i){ if (i>0) putchar(' '); printf("%d", vec[i]); } return 0; }

13|0L. Spicy Restaurant


乍一看很吓人,其实仔细看数据范围会发现点权很小

因此可以做100遍BFS,每次加入点权等于当前值的所有点,做一个多源BFS来更新到每个点的答案即可

#include<bits/stdc++.h> using namespace std; const int N = 5e5+5; const int INF = 0x3f3f3f3f; int n, m, q; struct Qry{ int p, ans; }qry[N]; vector<int> vec[105], ask[105]; vector<int> edg[N]; int dis[N]; int que[N], fr=0, ed=-1; signed main(){ ios::sync_with_stdio(0); cin.tie(0); cin >> n >> m >> q; for (int i=1; i<=n; ++i){ int w; cin>>w; vec[w].push_back(i); } for (int i=1; i<=m; ++i){ int u, v; cin >> u >> v; edg[u].push_back(v); edg[v].push_back(u); } for (int i=1; i<=q; ++i){ int p, a; cin >> p >> a; qry[i].p=p; ask[a].push_back(i); } memset(dis, 0x3f, (n+1)*sizeof(int)); for (int w=1; w<=100; ++w){ fr=0, ed=-1; for (int x : vec[w]) que[++ed]=x, dis[x]=0; while (fr<=ed){ int u=que[fr++]; for (int v : edg[u]){ if (dis[v]>dis[u]+1){ dis[v]=dis[u]+1; que[++ed]=v; } } } for (int id : ask[w]){ qry[id].ans = (dis[qry[id].p]<INF ? dis[qry[id].p] : -1); } } for (int i=1; i<=q; ++i) printf("%d\n", qry[i].ans); return 0; }

14|0M. True Story


题意初看不明所以,其实仔细一想会发现如果一个人已经选择出发了,那么他一定能坐上飞机

因此就是找一个最大的piti,每个人最多能用的时间就是这个,直接判断即可

#include<bits/stdc++.h> using namespace std; #define int long long const int N = 1e5+5; int p[N], t[N], s[N]; signed main(){ ios::sync_with_stdio(0); cin.tie(0); int n, k, x; cin >> n >> k >> x >> p[0]; for (int i=1; i<=n; ++i) cin >> s[i]; for (int i=1; i<=k; ++i) cin >> t[i]; for (int i=1; i<=k; ++i) cin >> p[i]; int ret=0; for (int i=0; i<=k; ++i){ ret=max(ret, p[i]-t[i]); } int cnt=0; for (int i=1; i<=n; ++i){ if (ret*s[i]>=x) ++cnt; } cout << cnt << '\n'; return 0; }

15|0Postscript


这场博客写的巨快,因为没啥难的题

不知道是省赛都是这个难度还是这场特别简单,久违地写了两位数的题


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/17871758.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(90)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
历史上的今天:
2020-12-02 Luogu P6359 [CEOI2018]Cloud computing
2020-12-02 CF582E Boolean Function
2020-12-02 Luogu P4800 [CEOI2015 Day2]核能国度
2020-12-02 AtCoder Grand Contest 015
2018-12-02 Luogu P2336 [SCOI2012]喵星球上的点名
点击右上角即可分享
微信分享提示