湖南多销 10
VP
张ls没来,所以就是我和左ls一起打
不得不说,左老师确实牛逼,大B题计算几何他是真会
然后这场我的策略就是跟榜做,然后开到傻比图论题卡死了
然后就是狂爆罚时,所以真的要当后置大脑了吗
题解
E
sb签到题
M
考虑\(1,2,3\)能组成的最大数字
并不显然是\((1+2)\cdot 3=9\)
所以如果\(d\geq 10\)那么这就是显然的
否则随便构造一组像\(97,43,13\)这样子的
三个质数,各自差二三十这样子,然后就可以随便过
G
简单数论题
记\(cnt\)为\(n\)的十进制位数。
由于\(cnt-1\)位的时候即使每个位都是\(9\)组的数也小于\(n\)
所以每个数至少有\(cnt-1\)个
这\(cnt\)位每个位都是\(i\)的时候组的数是否大于\(n\)决定是否要多一个。
注意\(0\)至多使用\(cnt-1\)个,所以要特判\(n\geq 10\)的时候答案要减一
L
考虑到多个合法括号序列拼接起来还是合法括号序列
我们只需要找一个断点让它前后两部分都是合法括号序列即可。
这个过程就是括号匹配的过程
然后就是断环成链翻二倍,做个哈希判一下是否相等就行。
题解给的证明是只用看第一个合法断点就行。
如果是哈希的话那就很无脑
注意一定要断环成链不能只判前后
三模哈希
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int o=2222222,base=233,mod[3]={1000000007,1000000009,998244353};
stack<int>q;
char s[o];
int pos,n,h[3][o],p[3][o];
int P(int x,int y,int m){
int ans=1;
while(y){
if(y&1)ans=ans*x%m;
x=x*x%m;
y>>=1;
}
return ans;
}
void pre(){
for(int j=0;j<3;j++){
p[j][0]=1;
for(int i=1;i<=n;i++){
p[j][i]=p[j][i-1]*base%mod[j];
h[j][i]=(h[j][i-1]+s[i]*p[j][i]%mod[j])%mod[j];
}
}
}
bool cmp(int i){
for(int j=0;j<3;j++){
if(h[j][n]!=(h[j][i+n]-h[j][i]+mod[j])%mod[j]*P(p[j][i],mod[j]-2,mod[j])%mod[j])return 0;
}
return 1;
}
void in(){
scanf("%s",s+1);
}
void work(){
pos=0,n=strlen(s+1);
for(int i=1;i<=n;i++)s[i+n]=s[i];
n*=2;
pre();
n/=2;
for(int i=1;i<=n;i++){
if(s[i]=='(')q.push(i);
else q.pop();
if(q.empty()){
if(!cmp(i)){
pos=i;
break;
}
}
}
}
void out(){
if(pos==0)puts("no");
else{
for(int i=pos+1;i<=n;i++)printf("%c",s[i]);
for(int i=1;i<=pos;i++)printf("%c",s[i]);
}
}
#undef int
int main(){
in(),work(),out();
return 0;
}
I
直接一个set维护所有空莲花,
lower_bound()找一下最小的那个空莲花,直接换掉就行
考虑到事实上空莲花的数量只有\(10^6\),所以相当可行。
时间复杂度\(O(n\log n)\)
然后是一个不会set版的回答。
如果有青蛙为\(1\),没青蛙为\(0\)
那么可以通过前缀和二分答案的方式找到最近的\(pos\)
上一个树状数组维护一下即可
时间复杂度\(O(n\log^2 n)\)
树状数组倍增可以做到\(O(n\log n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int o=2222222;
int n,q,mx,c[o];
struct BIT{
int t[o];
int lb(int x){return x&(-x);}
void add(int x,int val){
while(x<=mx){
t[x]+=val;
x+=lb(x);
}
}
int ques(int x){
int ans=0;
while(x){
ans+=t[x];
x-=lb(x);
}
return ans;
}
}T;
bool check(int mid,int pos){
return T.ques(mid)-T.ques(pos-1)==mid-pos+1;
}
void in(){
cin>>n;mx=2e6;
for(int i=1;i<=n;i++){
cin>>c[i];
T.add(c[i],1);
}
}
void work(){
cin>>q;
for(int i=1,x;i<=q;i++){
cin>>x;
int l=c[x],r=mx;
while(l<r){
int mid=(l+r)/2;
if(check(mid,c[x]))l=mid+1;
else r=mid;
}
int ans=l;
T.add(c[x],-1);
T.add(ans,1);
cout<<ans<<"\n";
c[x]=l;
}
}
int main(){
in();work();
return 0;
}
C
其实只有期望最短的那条才是要找的那条。
所以就是要么走随机传送下期望最短的节点,
要么就是直接不传送走最短路
也没有什么这那的讲究
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int o=2222222;
#define int long long
struct Graph{
struct edge{
int t,n;
}p[o];
vector<int>d[o];
int v[o],h[o],cnt;
void add(int s,int t){
cnt++;
p[cnt].t=t;
p[cnt].n=h[s];
h[s]=cnt;
}
void bfs(int s,int n){
memset(v,0,sizeof(v));
for(int i=0;i<=n;i++)d[s].push_back(0);
queue<int>q;
v[s]=1,q.push(s);
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=h[x];i;i=p[i].n){
int y=p[i].t;
if(v[y])continue;
d[s][y]=d[s][x]+1;
v[y]=1;
q.push(y);
}
}
}
int ques(int x,int s){
return d[s][x];
}
}G;
int n,m,k,fz,fm;
vector<int>l;
bool c[o];
int read(){
int i=1,j=0;
char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-')i=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
j=j*10+ch-48;
ch=getchar();
}
return i*j;
}
void in(){
n=read(),m=read(),k=read();
for(int i=1,x;i<=k;i++){
x=read();
l.push_back(x);
}
for(int i=1;i<=m;i++){
int x=read(),y=read();
G.add(x,y),G.add(y,x);
}
}
void work(){
G.bfs(1,n),G.bfs(n,n);
int val=0;fz=1e18;
for(auto i:l)val+=G.ques(i,n);
for(auto i:l)fz=min(fz,val-G.ques(i,n)+(k-1)*G.ques(i,1));
fz=min(fz,G.ques(1,n)*(k-1));
fm=(k-1);
int now=__gcd(fz,fm);
fz/=now,fm/=now;
}
void out(){
cout<<fz<<"/"<<fm<<"\n";
}
#undef int
int main(){
in(),work(),out();
return 0;
}
D
这个是一个经典的背包DP
不过要上一个压位高精度
或者考虑一个概率DP的模型也可以。
不过这个投骰子的模型还是太经典了,它非常像二项分布
所以拿二项分布的结论过来写就行了
K
直接考虑合法的部分不能从合法状态推下一个合法状态的转移方程
考虑翻转,设恰好第\(i\)家要造反的方案数\(f_i\)
那么前\(i\)家分配\(\sum\limits_{j=1}^{i}k_j=s_i\)个大数,随便排序
这一部分是\(s_i!\)
然后考虑前\(i-1\)家都不能造反,前面的话就不能全排\(s_{i-1},s_{i-2},s_{i-3},...,s_{1}\)个大数
然后推\(i-1\)的话也是一样,不能排\(s_{i-2},...,s_{1}\)的大数
这样的话递归/记搜的结构就出来了。
如果前\(j\)家要造反的话,那么\(j\)到\(i\)的排列是没有限制的
所以,前\(i\)家里前\(j\)家造反的方案数是\(f_j\cdot(s_i-s_j)!\)
两部分结合就可以得到\(f_i=s_i!-\sum\limits^{i}_{j=1}f_j\cdot{(s_i-s_j)!}\)
然后就可以得到最后的答案
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int o=2222222,mod=1e9+7;
int a[o],h,n,s[o],f[o],jc[o],ans;
int dfs(int sum,int pos){
if(pos==0)return 0;
if(f[pos]!=-1)return f[pos];
f[pos]=jc[sum];
for(int i=1;i<pos;i++)f[pos]=((f[pos]-dfs(s[i],i)*jc[sum-s[i]]%mod)+mod)%mod;
return f[pos];
}
void in(){
cin>>n>>h;
for(int i=1;i<=h;i++){
cin>>a[i];
s[i]=a[i]+s[i-1];
}
}
void work(){
jc[0]=1;
for(int i=1;i<=h;i++)f[i]=-1;
for(int i=1;i<=n;i++)jc[i]=jc[i-1]*i%mod;
dfs(s[h],h);
ans=jc[n];
for(int i=1;i<=h;i++)ans=((ans-dfs(s[i],i)*jc[n-s[i]]%mod)+mod)%mod;
}
void out(){cout<<ans;}
#undef int
int main(){
in(),work(),out();
return 0;
}
H
首先观察到长度为\(i\)的上下两位都是空的段,
停车方案数\(f_i=f_{i-1}+f_{i-2}\)
就是横着停和竖着停
初值\(f_1=1,f_0=1\)
那这就是长度为\(i\)的方案数是\(fib_i\)
既然知道这些,那么其实剩下的就是找一个集合\(S\),满足
接下来就是最牛逼的操作
随机化meet in the middle
首先meet in the middle
把\(200\)个数的集合分成前后两半
然后各随机抽\(10^6\)次
总共可以抽出\(10^{12}\)种组合
但是值域只有\(10^9+7\)
所以说很难抽不中。
时间复杂度也是稳过的。

浙公网安备 33010602011771号