暑假集训CSP提高模拟1
暑假集训CSP提高模拟1
组题人: @Delov
\(T1\) T2687. Start
-
大模拟。
-
需要注意的地方。
- 整数除法是向零取整,右移运算是向下取整,处理负数时会有差异,建议使用
floor(...)
。 - 给定的牌的顺序是从上至下,若要形成一个栈的话需要反向加入。
- 打完一张牌后迅速摸牌。
- 处于 \(\tt{DOUBLE}\) 状态下打出的第二张牌不再受 \(\tt{DOUBLE}\) 状态限制。
- 判断普通牌时不要把解牌一并算入。
点击查看代码
struct people { string name; map<string,int>card; }a[40]; string ls; deque<string>s; map<string,int>::iterator it; int card_to(string s,int p) { if(s=="A1"){return p+1;} if(s=="A2"){return p+2;} if(s=="A5"){return p+5;} if(s=="A9"){return p+9;} if(s=="A19"){return p+19;} if(s=="A49"){return p+49;} if(s=="A99"){return p+99;} if(s=="B1"){return p-1;} if(s=="B9"){return p-9;} if(s=="B19"){return p-19;} if(s=="C2"){return p*2;} if(s=="D2"){return floor(1.0*p/2);} if(s=="E0"){return 0;} if(s=="E49"){return 49;} if(s=="E99"){return 99;} return 114514; } int card_in(string s,int flag) { if(s=="PASS"||s=="TURN"||s=="DOUBLE"){return -1;} if(s[0]=='A'){return (flag==0)?2:3;} if(s[0]=='B'){return (flag==0)?3:2;} if(s[0]=='C'){return (flag==0)?1:4;} if(s[0]=='D'){return (flag==0)?4:1;} if(s[0]=='E'){return (flag==0)?5:5;} return 114514; } int check_exist(int pos,string s) { return a[pos].card[s]>=1; } int del_card(int pos,string s,int p) { if(check_exist(pos,s)==0) { return 0; } else { a[pos].card[s]--; cout<<a[pos].name<<" used "<<s<<",now p="<<p<<"."<<endl; return 1; } } int basic_card(int pos,int &p,int flag) { int sum=(flag==0)?-0x7f7f7f7f:0x7f7f7f7f; string ans; if(flag==0) { for(it=a[pos].card.begin();it!=a[pos].card.end();it++) { if(check_exist(pos,it->first)==1&&card_in(it->first,flag)!=-1) { if(sum<card_to(it->first,p)&&card_to(it->first,p)<=99) { sum=card_to(it->first,p); ans=it->first; } else { if(card_to(it->first,p)==sum&&card_in(ans,flag)>card_in(it->first,flag)) { ans=it->first; } } } } } else { for(it=a[pos].card.begin();it!=a[pos].card.end();it++) { if(check_exist(pos,it->first)==1&&card_in(it->first,flag)!=-1) { if(sum>card_to(it->first,p)&&card_to(it->first,p)<=99) { sum=card_to(it->first,p); ans=it->first; } else { if(card_to(it->first,p)==sum&&card_in(ans,flag)>card_in(it->first,flag)) { ans=it->first; } } } } } if(sum!=((flag==0)?-0x7f7f7f7f:0x7f7f7f7f)) { p=sum; del_card(pos,ans,p); return 1; } else { return 0; } } int resolve_card(int pos,int p,int &dir,int &flag) { if(del_card(pos,"PASS",p)==1){return 1;} if(del_card(pos,"TURN",p)==1){dir^=1;return 1;} if(del_card(pos,"DOUBLE",p)==1){flag=1;return 1;} return 0; } void get_card(int pos) { a[pos].card[s.front()]++; s.pop_front(); } int main() { int n,m,k,p,flag,dir,pos=1,i,j; cin>>n>>m>>k; for(i=1;i<=n;i++) { cin>>a[i].name; for(j=1;j<=3;j++) { cin>>ls; a[i].card[ls]++; } } for(i=1;i<=k;i++) { cin>>ls; s.push_back(ls); } for(i=1;i<=m;i++) { cout<<"Round "<<i<<":"<<endl; p=flag=0; dir=1; while(1) { if(flag==0) { if(basic_card(pos,p,flag)==0&&resolve_card(pos,p,dir,flag)==0) { break; } else { get_card(pos); } } else { if(resolve_card(pos,p,dir,flag)==0) { if(basic_card(pos,p,flag)==0) { break; } else { flag=0; get_card(pos); if(basic_card(pos,p,flag)==0) { if(resolve_card(pos,p,dir,flag)==0) { break; } else { get_card(pos); } } else { flag=0; get_card(pos); } } } else { get_card(pos); } } if(dir==1) { pos++; if(pos==n+1) { pos=1; } } else { pos--; if(pos==0) { pos=n; } } } cout<<a[pos].name<<" lost the game."<<endl; a[pos].card.clear(); for(j=1;j<=3;j++) { a[pos].card[s.front()]++; s.pop_front(); } } return 0; }
- 整数除法是向零取整,右移运算是向下取整,处理负数时会有差异,建议使用
\(T2\) T807. mine
-
设 \(f_{i,0/1/2/3/4}\) 分别表示处理到第 \(i\) 位时,第 \(i\) 位为雷/第 \(i\) 位不为雷,第 \(i-1,i+1\) 位不为雷/第 \(i\) 位不为雷,第 \(i-1\) 位不为雷,第 \(i+1\) 位为雷/第 \(i\) 位不为雷,第 \(i-1\) 位为雷,第 \(i+1\) 位不为雷/第 \(i\) 位不为雷,第 \(i-1,i+1\) 位为雷的方案数,状态转移方程(如果存在这个状态的话)为 \(\begin{cases} f_{i,0}=f_{i-1,0}+f_{i-1,2}+f_{i-1,4} \\ f_{i,1}=f_{i-1,1}+f_{i-1,3} \\ f_{i,2}=f_{i-1,1}+f_{i-1,3} \\ f_{i,3}=f_{i-1,0} \\ f_{i,4}=f_{i-1,0} \end{cases}\) ,边界为 \(f_{0,1}=f_{0,2}=1\) 。
-
最终,有 \(f_{|s|,0}+f_{|s|,1}+f_{|s|,3}\) 即为所求。
点击查看代码
const ll p=1000000007; ll f[1000010][5]; char s[1000010]; int main() { ll n,i; cin>>(s+1); n=strlen(s+1); f[0][1]=f[0][2]=1; for(i=1;i<=n;i++) { if(s[i]=='?') { f[i][0]=(f[i-1][0]+f[i-1][2]+f[i-1][4])%p; f[i][1]=(f[i-1][1]+f[i-1][3])%p; f[i][2]=(f[i-1][1]+f[i-1][3])%p; f[i][3]=f[i-1][0]; f[i][4]=f[i-1][0]; } if(s[i]=='*') { f[i][0]=(f[i-1][0]+f[i-1][2]+f[i-1][4])%p; } if(s[i]=='0') { f[i][1]=(f[i-1][1]+f[i-1][3])%p; } if(s[i]=='1') { f[i][2]=(f[i-1][1]+f[i-1][3])%p; f[i][3]=f[i-1][0]; } if(s[i]=='2') { f[i][4]=f[i-1][0]; } } cout<<(f[n][0]+f[n][1]+f[n][3])%p<<endl; return 0; }
-
貌似还有列出方程组后,手动解带状矩阵,求自由元数量的高级做法,但我不会,暂时咕了。
\(T3\) T2790. 小凯的疑惑
-
最终能组成的数一定能表示成 \(k \times \gcd(x,y)(k \in \mathbb{N})\) 的形式,即最终能组成的数 \(\bmod \gcd(x,y)=0\) 。
-
当 \(d \ne 1\) 时存在无数个正整数不能被如此表示。
-
当 \(d=1\) 时
- 由弱化版 luogu P3951 [NOIP2017 提高组] 小凯的疑惑 / [蓝桥杯 2013 省] 买不到的数目 的结论,有不能被 \(x,y\) 表示的最大整数为 \(xy-x-y\) 。
- 又因为剩余系的性质,有当 \(a\) 跑遍模 \(y\) 的完全剩余系时 \(ax\) 也跑遍模 \(y\) 的完全剩余系。
- 考虑从完全剩余系入手,枚举 \(a \in [0,y)\) 即可不重不漏地算完。
- 设 \(c=ax+by(b \le 0 \le a)\) ,移项有 \(ax-c=-by\) 。当 \(ax\) 确定时,因为 \(c \ge 1\) 有 \(-b \in [1, \left\lfloor \dfrac{ax}{y} \right\rfloor]\) 。
- 最终,有 \(\begin{aligned} \sum\limits_{i=0}^{y-1} \left\lfloor \frac{ix}{y} \right\rfloor &=\frac{\sum\limits_{i=0}^{y-1} (ix)-\sum\limits_{i=0}^{y-1} (ix \bmod y)}{y} \\ &=\frac{x \times \sum\limits_{i=0}^{y-1}i-\sum\limits_{i=0}^{y-1}i}{y} \\ &=\frac{\frac{xy(y-1)}{2}-\frac{y(y-1)}{2}}{y} \\ &=\frac{(x-1)(y-1)}{2} \end{aligned}\) 即为所求。
点击查看代码
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; } int main() { ll x,y; cin>>x>>y; cout<<(gcd(x,y)==1?(x-1)*(y-1)/2:-1)<<endl; return 0; }
\(T4\) T2810. 春节十二响
-
对于以 \(x\) 为根的子树内的节点分段时一定是和以 \(fa_{x}\) 为根的子树内除以 \(x\) 为根的子树外的其他子树的节点在同一个段。
-
从贪心的角度分析,大的数和大的数在一起一定是最优的,优先队列或可并堆维护。
-
启发式合并维护即可。
点击查看代码
struct node { ll nxt,to; }e[200010]; ll head[200010],a[200010],cnt=0; priority_queue<ll>q[200010],ls; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dsu_merge(ll x,ll y) { if(q[x].size()<q[y].size()) { swap(q[x],q[y]); } while(q[y].empty()==0) { ls.push(max(q[x].top(),q[y].top())); q[x].pop(); q[y].pop(); } while(ls.empty()==0) { q[x].push(ls.top()); ls.pop(); } } void dfs(ll x) { for(ll i=head[x];i!=0;i=e[i].nxt) { dfs(e[i].to); dsu_merge(x,e[i].to); } q[x].push(a[x]); } int main() { ll n,u,v,ans=0,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; } for(i=2;i<=n;i++) { cin>>u; v=i; add(u,v); } dfs(1); while(q[1].empty()==0) { ans+=q[1].top(); q[1].pop(); } cout<<ans<<endl; return 0; }
总结
- \(T1\) 多次读假题。
- \(T3\) 记错原题条件了,导致没想到会有无解的情况。
后记
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18309828,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。