比赛链接:这里
OP
个人能力有限,有的题目实在知识点欠缺太多,这里是大佬的题解
A 九峰与签到题
签到题
思路
持续模拟打标记即可,注意题目描述:他认为任意时间内通过率大于等于50%的题为签到题
代码
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
| #include <bits/stdc++.h> using namespace std; typedef long long ll;
int main() { int m,n,i,j; int ac[25]={0},sb[25]={0}; bool pro[25]; char g[10]; int gq; cin>>m>>n; for(i=1;i<=n;i++)pro[i]=1; while(m--) { scanf("%d",&gq); sb[gq]++; cin>>g; if(!strcmp(g,"AC"))ac[gq]++; if(ac[gq]*1.0/sb[gq]<0.5)pro[gq]=0; } int f=0; for(i=1;i<=n;i++) { if(pro[i]&&sb[i]) { if(f)printf(" "); printf("%d",i),f=1; } } if(!f)printf("-1"); return 0; }
|
B 武辰延的字符串
哈希字符串、二段性二分
思路
这道题首先想到的就是 O(n2) 的二分,显然是要爆的,但是 sj 是有二段性的,即如果 sja 满足条件且 sj(a+1) 不满足,则所有的 sjp( p<=a ) 均满足,所有的 sjq( q>a ) 均不满足。
有了二段性,我们便可以进行二分。
对于二分的 check 过程,我们可以用哈希字符串在 O(1) 的时间复杂度内完成判断,关于哈希字符串,可以参照这里。
代码的细节还蛮多需要注意的。
代码
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include <bits/stdc++.h> using namespace std; typedef long long ll; string s,t; const int N=1e5+5,P=131; const ll M=1e9+7; ll a[N],b[N]; ll qm(ll a,ll b) { ll ans=1; while(b) { if(b&1)ans=ans*a%M; a=a*a%M; b>>=1; } return ans; } void makehash(string& str,ll aim[]) { int i; aim[0]=str[0]-'a'; for(i=1;str[i];i++) { aim[i]=aim[i-1]*P%M+str[i]-'a'; aim[i]%=M; } } ll gethash(ll aim[],int l,int r) { return (aim[r]+M-aim[l-1]*qm(P,r-l+1)%M)%M; } int main() { cin>>s; cin>>t; makehash(s,a); makehash(t,b); int i,j; ll ans=0; for(i=0;s[i]==t[i]&&s[i]&&t[i];i++) { int l=0,r=min(s.length(),t.length()-1-i),mid; while(l<r) { mid=l+r+1>>1; if(a[mid-1]==gethash(b,i+1,i+mid))l=mid; else r=mid-1; } ans+=l; } cout<<ans; return 0; }
|
D 温澈滢的狗狗
容斥,二分
思路
亲密值是狗狗编号差值,而不是颜色编号差值,在这里耽误了自己不少时间。
首先对于1e10的数据量,模拟是会爆的,而且选择的亲密值上界越高,狗狗对数就越多,存在单调性,所以选择二分进行计数;
我们需要通过二分找出一个最大的亲密值上界 l,使得满足亲密值小于等于此值的狗狗对数严格小于 k ,再对亲密值为 l+1 的狗狗对进行模拟;
如何计算亲密值小于等于 mid 的狗狗对数:
我们可以使用容斥思想,对数=编号差值小于等于mid的所有狗狗对数-编号差值小于等于mid的同色狗狗对数;
差值为1的狗狗有 n-1 对,差值为2有 n-2 对,…
依据等差数列求和公式,所有狗狗对数即为 2(n−1+n−x)∗x;
对于同色狗狗,我们可以在每个颜色的狗狗集合内使用滑窗思想,计算以集合内每只狗狗为最小值时,有多少差值小于等于mid的狗狗对,具体可以见注释。
至于非法输入判定:
如果cnt(mid)<=k,mid 值会持续右移,如果输入数据非法,mid 会持续右移至 r 的初始值,我只需要将 r 的初始值设为一个不合法的值,即可区分非法输入。
代码
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include<bits/stdc++.h> using namespace std; typedef long long ll;
ll n,k; int col[100005]; vector<int> num[100005];
ll cnt(int x) { ll sum=(2*n-1-x)*x/2; for(int i=1;i<=n;i++) { int r=0; for(int l=0;l<num[i].size();l++) { while(r<num[i].size()&&num[i][r]-num[i][l]<=x)r++; sum-=(r-l-1); } } return sum; }
int main() { cin>>n>>k; int i,g; for(i=1;i<=n;i++) { scanf("%d",&col[i]); num[col[i]].push_back(i); } int l=0,r=n+1,mid; while(l<r) { mid=l+r+1 >>1; if(cnt(mid)<k)l=mid; else r=mid-1; } if(l==n+1)printf("-1"); else { k-=cnt(l); for(i=1;i+1+l<=n;i++) { if(col[i]!=col[i+1+l])k--; if(k==0) { printf("%d %d",i,i+1+l); break; } } } return 0; }
|
E 九峰与子序列
哈希字符串,dp
思路
B题思路中有关于哈希字符串的简介链接。
本题类似于01背包问题,即对每个字符串有选与不选两种情况,n个字符串共同组成长度为∣k∣的串;
如果此字符串与目标字符串的对应段匹配,即选,否则即不选;
构建dp数组dp[i][j]即为使用前 i 个字串,能组成至目标串第 j 位的方法数;
dp[i][j]=dp[i−1][j]+dp[i−1][j−∣a[i]∣]∗(a[i]==k[j−∣a[i]∣+1,j])
判断字符串相等时用哈希字符串可以将时间复杂度降为 O(1);
遍历长度∣k∣时,需要从后向前,避免同一个字符串被使用多次;
时间复杂度为O(n|k|),卡的比较紧。
注意:函数外定义数组时不能直接初始化(p[5000006]={1})
有的编译环境也可以
代码
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 39 40 41 42 43 44 45 46 47
| #include<bits/stdc++.h> using namespace std; typedef long long ll;
const ll M=1e9+7,P=131,N=5000006; ll p[5000006];
ll gethash(ll aim[],ll l,ll r) { if(l==0)return aim[r]; return (aim[r]+M-aim[l-1]*p[r-l+1]%M)%M; }
int main() { char k[N],a[N]; ll z[N],ha; int n,i,j; cin>>n; scanf("%s",k); p[0]=1; z[0]=k[0]-'a'; for(i=1;i<strlen(k);i++) { p[i]=p[i-1]*P%M; z[i]=(z[i-1]*P+k[i]-'a')%M; } int dp[N]={1}; for(int i=1;i<=n;i++) { scanf("%s",a); ha=0; for(j=0;j<strlen(a);j++) { ha=(ha*P+a[j]-'a')%M; } for(j=strlen(k);j>=strlen(a);j--) { if(dp[j-strlen(a)]>0) { if(gethash(z,j-strlen(a)+1-1,j-1)==ha)dp[j]+=dp[j-strlen(a)]; } } } printf("%lld",dp[strlen(k)]); return 0; }
|
F 魏迟燕的自走棋
贪心,并查集
思路
这道题的一个关键就是ki∈{1,2}。
首先易得,优先配发增益更大的武器能使总增益值更大;
在具体分配时:
举例来说:
如果有一个武器可以分配给1,2,我们便可以认为此武器可以被使用,且可以在1,2之间随意调换,且空位为1;
若又有一武器可以分配给2,3,我们便可以认为此武器可以被使用,且这两个武器可以通过在1,2,3间调换使任一玩家无武器,空位为1;
若再有一武器可以分配给1,3,我们便可以认为此武器可以被使用,且使1,2,3均有武器,空位为0;
所以,一个武器可以串起总空位大于0的两个不同集合,且新集合空位数为旧集合空位和-1;
若一个武器可用范围是在同一集合的两个元素,如果该集合有空位,则此武器可用,空位-1;
对于可用人数为1的武器,我们可以认为可用元素是两个相同元素,便可以归类为上面的情况。
这个过程可以通过并查集实现。
初始化时,我们使得fa[i]=i
的同时,也要初始化lef[i]=1
。
我们可以发现,任何时候,任一集合空位数只能为0或1;
代码
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 39 40 41 42 43 44
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+5,M=1e9+7; int fa[N],lef[N]; struct node{ int a,b,w; }e[N]; bool cmp(node a,node b){ return a.w>b.w; } int find(int x) { if(fa[x]!=x)fa[x]=find(fa[x]); return fa[x]; } int main() { int i,n,m,k; ll ans=0; cin>>n>>m; for(i=1;i<=n;i++)fa[i]=i,lef[i]=1; for(i=1;i<=m;i++) { scanf("%d",&k); if(k==1)scanf("%d",&e[i].a),e[i].b=e[i].a; else scanf("%d%d",&e[i].a,&e[i].b); scanf("%d",&e[i].w); } sort(e+1,e+m+1,cmp); for(i=1;i<=m;i++) { int af=find(e[i].a),bf=find(e[i].b); if(lef[af]||lef[bf]) { ans+=e[i].w; if(af==bf)lef[af]=0; else fa[bf]=af,lef[af]+=lef[bf]-1; } } cout<<ans; return 0; }
|
G 九峰与蛇形填数
模拟
思路
这道题卡得比较狠:对m个矩阵依次填会爆,对每一个位置判断在哪一个小矩阵里再填还会爆。
所以我们要先找到能包含所有有值的位置的最小矩形,在此矩形中遍历每一个位置,判断在哪一个矩阵里再填数。
还有另一种方法,翻别人代码时发现的,就是反向枚举m个子矩阵,在对空格填数时,在该格打上一个”路标“,指示此矩阵在该行的最后一个数的位置,在再次扫过此格时可以直接跳到该位置,时间复杂度比上面的还小一些。
代码
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
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=2003,M=3003; int main() { static int n,m,a[N][N]= {0},bi[M],bj[M],k[M]; int i,j,p,q,pmi,pmx,qmi,qmx; cin>>n>>m; pmi=qmi=n; pmx=qmx=1; for(i=1; i<=m; i++) { scanf("%d%d%d",&bi[i],&bj[i],&k[i]); pmi=min(pmi,bi[i]),qmi=min(qmi,bj[i]); pmx=max(pmx,bi[i]+k[i]-1),qmx=max(qmx,bj[i]+k[i]+1); } for(p=pmi; p<=pmx; p++) for(q=qmi; q<=qmx; q++) for(i=m; i>=1; i--) if(p>=bi[i]&&p<=bi[i]+k[i]-1&&q>=bj[i]&&q<=bj[i]+k[i]-1) { if(p-bi[i]&1)a[p][q]=(p-bi[i]+1)*k[i]-(q-bj[i]); else a[p][q]=(p-bi[i])*k[i]+(q-bj[i]+1); break; } for(i=1; i<=n; i++) { for(j=1; j<=n; j++)printf("%d ",a[i][j]); printf("\n"); } return 0; }
|
注:这道题题干中x是行数,y是列数。
H 吴楚月的表达式
DFS
思路
此题中任一表达式均可用 a + b 表示:
如果运算为+n,则 a=a′+b′,b=n;
如果运算为−n,则 a=a′+b′,b=M−n(负数取模会出问题);
如果运算为∗n,则 a=a′,b=b′∗n;
如果运算为/n,则 a=a′,b=b′∗nM−2%M(除法取模会出问题,所以用逆元处理).
接下来从根部向下DFS即可。
代码
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+5,M=1e9+7; vector<ll>son[N]; ll num[N],ans[N],n; char o[N]; ll qm(ll a,ll b) { ll ans=1; for(;b;b>>=1) { if(b&1)ans=ans*a%M; a=a*a%M; } return ans; } void dfs(ll a,ll b,int no) { int i; if(o[no-2]=='*') { b=b*num[no]%M; } else if(o[no-2]=='/') { b=b*qm(num[no],M-2)%M; } else if(o[no-2]=='+') { a=(a+b)%M; b=num[no]%M; } else if(o[no-2]=='-') { a=(a+b)%M; b=-num[no]+M; } ans[no]=(a+b)%M; for(i=0;i<son[no].size();i++)dfs(a,b,son[no][i]); } int main() { ll i,j,g; scanf("%lld",&n); for(i=1;i<=n;i++)scanf("%lld",&num[i]); for(i=2;i<=n;i++){scanf("%lld",&g);son[g].push_back(i);} scanf("%s",o); ans[1]=num[1]; for(i=0;i<son[1].size();i++)dfs(0,num[1],son[1][i]); for(i=1;i<=n;i++)printf("%lld ",ans[i]); return 0; }
|
J 邬澄瑶的公约数
思路
用 pi 表示第 i 个质数,则任意整数均可表示为 a=∏piki;
则gcd(a1t1,a2t2,...)=∏pimin(ka1∗t1,ka2∗t2,...)
代码
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=10000; const ll M=1000000007; ll qm(ll a,ll b) { ll ans=1; while(b) { if(b&1)ans=ans*a%M; a=a*a%M; b>>=1; } return ans%M; } int main() { int ny[N],zy[N],i,j; for(i=1;i<N;i++)zy[i]=1000000; int n,gx[N+1],gp[N+1]; cin>>n; for(i=1;i<=n;i++) { scanf("%d",&gx[i]); } for(i=1;i<=n;i++) { scanf("%d",&gp[i]); } for(i=1;i<=n;i++) { memset(ny,0,sizeof ny); for(j=2;j<=gx[i];j++) { while(gx[i]%j==0) { ny[j]++; gx[i]/=j; } } for(j=2;j<N;j++) { zy[j]=min(zy[j],ny[j]*gp[i]); } } ll ans=1; for(j=2;j<N;j++) { ans*=qm(j,zy[j]); ans%=M; } cout<<ans; return 0; }
|
ED
\