NOIP模拟赛
今天想着反正高一高二都要考试,那么干脆跟着高二考吧,因为高二的比赛更有技术含量(我自己带的键盘放在这里).
今天考了一套英文题?发现阅读理解还是有一些困难的.
T1:有$n$个点,$m$天,第$[1,m]$天每天会把最大公约数是$m-i+1$的数连边,给出$q$组询问,询问给出的两点最早被连接起来是什么时候.$n,m,q<=10^5$
一开始以为是一道求$gcd$的签到题...后来发现还可以这样:$3->6,6->8$,所以不用等到最后一天$3,8$就连通了.
首先可以想到枚举每一天,把$gcd$为这个数的数对两两连边合并并查集,但是查询的时候就不知道是哪天连起来的了,所以可以用$Kruscal$重构树,每次合并时加一个新的点来中转.但是枚举数对的复杂度非常高,有一种思路是枚举质数对并乘上$gcd$,可是打表发现这个范围内的质数有点多...
这时可以发现一个很奇妙的性质:如果两个数同时是$d$的倍数,那么它们被连接起来不会晚于$d$的出现时间,为什么?依旧采取最大公约数的表示形式:
$$a=xd,b=yd,(x,y)不保证互质$$ $$a=xd,b=yd$$ $$a=x_1qd,b=y_1qd \quad (x_1,y_1)=1$$ $$(x_1d,y_1d)=d$$ $$(a,x_1d)=x_1d,(b,y_1d)=y_1d$$ $$min\left \{ d,x_1d,x_2d\right \}=d$$
既然$d$的倍数都会被连起来,我们又是每次建立新点来连接,就可以直接把所有倍数都连接到新点上,不用枚举数对了.
树建好之后每次询问在树上倍增找$LCA$即可.
1 # include2 # include 3 # include 4 # include <string> 5 # include 6 # include 7 # define R register int 8 # define ll long long 9 10 using namespace std; 11 12 const int maxn=200005; 13 int x,n,m,q,h,firs[maxn],f[maxn][21]; 14 int dep[maxn],v[maxn],nod,F[maxn]; 15 struct edge 16 { 17 int too,nex; 18 }g[maxn*2+5]; 19 20 int read () 21 { 22 int x=0,f=1; 23 char c=getchar(); 24 while (!isdigit(c)) { if(c=='-') f=-f; c=getchar(); } 25 while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar(); 26 return x*f; 27 } 28 29 void add (int x,int y) 30 { 31 g[++h].nex=firs[x]; 32 g[h].too=y; 33 firs[x]=h; 34 } 35 36 int father (int x) 37 { 38 if(x!=F[x]) return F[x]=father(F[x]); 39 return x; 40 } 41 42 void dfs (int x) 43 { 44 int j; 45 for (R i=firs[x];i;i=g[i].nex) 46 { 47 j=g[i].too; 48 if(dep[j]) continue; 49 f[j][0]=x; 50 dep[j]=dep[x]+1; 51 for (R k=1;k<=20;++k) 52 f[j][k]=f[ f[j][k-1] ][k-1]; 53 dfs(j); 54 } 55 } 56 57 int lca (int x,int y) 58 { 59 if(dep[x]>dep[y]) swap(x,y); 60 for (R i=20;i>=0;--i) 61 if(dep[y]-(1<=dep[x]) y=f[y][i]; 62 if(x==y) return x; 63 for (R i=20;i>=0;--i) 64 { 65 if(f[x][i]==f[y][i]) continue; 66 x=f[x][i]; 67 y=f[y][i]; 68 } 69 return f[x][0]; 70 } 71 72 int main() 73 { 74 scanf("%d%d%d",&n,&m,&q); 75 for (R i=1;i<=n;++i) 76 F[i]=i; 77 nod=n; 78 for (R i=m;i>=1;--i) 79 { 80 nod++; 81 F[nod]=nod; 82 v[nod]=m-i+1; 83 for (R j=1;j*i<=n;++j) 84 { 85 x=father(i*j); 86 if(x==nod) continue; 87 add(nod,x); 88 add(x,nod); 89 F[x]=nod; 90 } 91 } 92 dep[nod]=1; 93 dfs(nod); 94 for (R i=1;i<=q;++i) 95 { 96 int x,y; 97 x=read(),y=read(); 98 printf("%d\n",v[ lca(x,y) ]); 99 } 100 fclose(stdin); 101 fclose(stdout); 102 return 0; 103 }
T2:有$n$座楼,每座有一个高度和金币数,可以从任意楼开始跳,每次只能跳到右边不低于当前楼高的楼上,可以在任意楼停下,问获得的总金币数大于等于$k$的方案数.$n<=40$
考场写了搜索,还有一种奇怪的用$map$做$dp$的做法,正解是折半搜索,分别找到前后两部分的答案数,然后记录前半部分以高度$i$结尾,收益是$j$的方案数以及后半部分以高度$i$开始,收益是$j$的方案数,排序后对于前者找到后者的匹配区间,好像很快啊.
1 # include2 # include 3 # include 4 # include <string> 5 # include 6 # include 7 # include 8 # include
T3:https://www.luogu.org/problemnew/show/P4441
$Cena$不能编译$string$类型...?直接全$T$了...
可以尝试带着$string$一起$dp$,保存目前在哪个点,有多少个未匹配的左括号的最长长度,$string$中保存这个答案,显然这样是比较慢的...在洛谷上也没过。题解是$dp$时只求长度,最终拿最长长度倒推回去找答案,听起来很有道理的样子.
1 # include2 # include 3 # include 4 # include <string> 5 # include 6 # include 7 # define R register int 8 # define ll long long 9 10 using namespace std; 11 12 int read () 13 { 14 int x=0,f=1; 15 char c=getchar(); 16 while (!isdigit(c)) { if(c=='-') f=-f; c=getchar(); } 17 while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar(); 18 return x*f; 19 } 20 21 const int dir[]={-1,0,1}; 22 int a[305][305],pos,x; 23 int dp[2][305][305]; 24 string s[2][305][305],c; 25 string ans; 26 int len; 27 28 string tr (string a,string b) 29 { 30 if(a.length()>b.length()) return a; 31 else if(a.length() return b; 32 return min(a,b); 33 } 34 35 int main() 36 { 37 int n,m; 38 cin>>n>>m; 39 for (R i=n;i>=1;--i) 40 { 41 cin>>c; 42 while(c.length()==0) cin>>c; 43 c=' '+c; 44 for (R j=1;j<=m;++j) 45 { 46 if(c[j]=='.') a[i][j]=0; 47 else if(c[j]=='(') a[i][j]=1; 48 else if(c[j]==')') a[i][j]=-1; 49 else if(c[j]=='*') a[i][j]=100; 50 else pos=j; 51 } 52 } 53 for (R i=0;i<=m+1;++i) 54 a[n+1][i]=100; 55 memset(dp[1],128,sizeof(dp[1])); 56 dp[1][0][pos]=0; 57 for (R i=2;i<=n+1;++i) 58 { 59 int no=i&1; 60 int las=no^1; 61 memset(dp[no],128,sizeof(dp[no])); 62 for (R j=1;j<=m;++j) 63 for (R k=1;k<=m;++k) 64 s[no][j][k]=""; 65 for (R j=0;j<=m;++j) 66 for (R k=1;k<=m;++k) 67 { 68 if(dp[las][j][k]<0) continue; 69 for (R d=0;d<3;++d) 70 { 71 x=k+dir[d]; 72 if(x<=0||x>m) continue; 73 if(a[i][x]==100) 74 { 75 if(j==0) ans=tr(ans,s[las][j][k]); 76 continue; 77 } 78 if(a[i][x]==0) 79 { 80 if(dp[no][j][x]<dp[las][j][k]) 81 { 82 dp[no][j][x]=dp[las][j][k]; 83 s[no][j][x]=s[las][j][k]; 84 } 85 if(dp[no][j][x]==dp[las][j][k]) s[no][j][x]=tr(s[no][j][x],s[las][j][k]); 86 } 87 if(a[i][x]==-1) 88 { 89 if(j==0) continue; 90 if(dp[no][j-1][x] 1) 91 { 92 dp[no][j-1][x]=dp[las][j][k]+1; 93 s[no][j-1][x]=s[las][j][k]+")"; 94 } 95 if(dp[no][j-1][x]==dp[las][j][k]+1) s[no][j-1][x]=tr(s[no][j-1][x],s[las][j][k]+")"); 96 } 97 if(a[i][x]==1) 98 { 99 if(dp[no][j+1][x] 1) 100 { 101 dp[no][j+1][x]=dp[las][j][k]+1; 102 s[no][j+1][x]=s[las][j][k]+"("; 103 } 104 if(dp[no][j+1][x]==dp[las][j][k]+1) s[no][j+1][x]=tr(s[no][j+1][x],s[las][j][k]+"("); 105 } 106 } 107 } 108 } 109 cout< ans; 110 return 0; 111 }
---shzr