打 * 号表示没有写的题,可能是不打算写了,也可能是暂时没来得及写。
A. AMPPZ in the times of disease 难度: $\bigstar\bigstar$
题目大意:
给定 $n$ 个平面上的点,你需要划分成 $k$ 个非空点集,使得任意一对同一集合内的点的距离小于任意一对不同集合内点的距离,保证有解。
$n\leq 2\times 10^6, k\leq 20$
题目解法:
随便找一个点 $p_1$,再求出其余点与 $p_1$ 距离最远的点 $p_2$,再求出其余点和 $p_1,p_2$ 距离最小值最大的点 $p_3$,以此类推。
易证 $p_1,\ldots,p_k$ 属于不同集合,将每个其余点分进距离最近的 $p_i$ 所在集合即可。
时间复杂度为 $O(nk)$。
代码
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 #include <bits/stdc++.h> #define ll long long using namespace std;const int N=2000010 ;int t,n,k,c[N],vis[N];ll x[N],y[N],mn[N]; int read () { int res=0 ; char c=getchar (); while (c<'0' ||c>'9' ) {c=getchar ();} while (c>='0' &&c<='9' ) { res=res*10 -'0' +c; c=getchar (); } return res; } ll dis (int a,int b) {return (x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]);}void calc (int x,int bel) { for (int j=1 ;j<=n;j++) { if (vis[j]) {continue ;} ll d=dis (x,j); if (d<mn[j]) {mn[j]=d,c[j]=bel;} } } int main () { t=read (); for (int ii=1 ;ii<=t;ii++) { n=read ();k=read (); for (int i=1 ;i<=n;i++) { mn[i]=6e18 ,c[i]=vis[i]=0 ; x[i]=read ();y[i]=read (); } c[1 ]=1 ,vis[1 ]=1 ; int las=1 ; calc (las,1 ); for (int i=2 ;i<=k;i++) { int nw=0 ; for (int j=1 ;j<=n;j++) { if (vis[j]) {continue ;} if (mn[j]>mn[nw]) {nw=j;} } c[nw]=i,vis[nw]=1 ; las=nw; calc (nw,i); } for (int i=1 ;i<=n;i++) {printf ("%d " ,c[i]);} printf ("\n" ); } return 0 ; }
B. Babushka and her pierogi 难度: $\bigstar\bigstar\bigstar$
题目大意:
给定长度为 $n$ 的序列 $a_{1\ldots n}$,所有数两两不同,你需要通过若干次操作将其变为其某个给定排列 $b_{1\ldots n}$,每次操作可以交换两个数 $a_i,a_j$,代价为 $C+|a_i-a_j|$,其中 $C$ 为给定常数。求最小代价和。
$n\leq 2\times 10^5$
题目解法:
答案有一个下界 $(n-c)\times C+\dfrac{1}{2}\sum\limits_{i=1}^n|a_i-b_i|$,其中 $c$ 是置换环个数,有很多种方法得出这个下界,并且猜想这个下界可以达到。
考虑一次交换 $a_i,a_j$,它们在同一置换环上,设 $b_i<b_j$,如果 $a_i\in (a_j,b_j]$ 且 $a_j\in [b_i,a_i)$ 则称这是一次好交换。容易发现为了达到下界要求每次操作都是好交换,同时这也是充分条件。
剩下的一个结论是:考虑最小的 $b_i$ 使得 $a_i\ne b_i$,一定存在一组好交换 $(a_i,a_j)$,证明是考虑满足 $a_j<a_i$ 的 $a_j$ 共有 $a_i-1$ 个,而 $b_j<a_i$ 的 $b_j$ 只有 $a_i-2$ 个(把空位挖掉)。
所以我们从小到大考虑每个 $b_i$,如果已经有 $a_i=b_i$ 则跳过,否则找出一个包含 $a_i$ 的好交换并进行之。
考虑如何在比较好的复杂度内找出这样的一个好交换,题解的思路颇为巧妙:从 $b_i$ 所在置换环向 $b_i$ 两边同步搜索,直到遇到一个好交换,这时如果原本长度为 $l$ 的置换环分解成长度为 $a,b$ 的置换环,则只进行了 $\min(a,b)$ 次检验,相当于启发式分裂,所以时间复杂度为 $O(n\log n)$。
还有很多种方法解决最后求好交换的问题,不过要做到 $O(n\log n)$ 并不容易。
代码
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 #include <bits/stdc++.h> #define ll long long using namespace std;const int N=200010 ;struct P {int a,b,id;} p[N];int t,n,c,cur,pre[N],nx[N];ll ans; vector < pair<int ,int > > res; map <int ,int > mp; bool cmp (P a,P b) {return a.b<b.b;}bool chk (int x,int y) {return (p[y].a<p[x].a&&p[x].a<=p[y].b);}void swp (int x,int y) { pre[nx[x]]=y,pre[nx[y]]=x; swap (nx[x],nx[y]); swap (p[x].a,p[y].a); ans+=c+abs (p[x].a-p[y].a); res.push_back (make_pair (p[x].id,p[y].id)); } int main () { scanf ("%d" ,&t); for (int ii=1 ;ii<=t;ii++) { mp.clear (); scanf ("%d%d" ,&n,&c); for (int i=1 ;i<=n;i++) {scanf ("%d%d" ,&p[i].a,&p[i].b);p[i].id=i;} sort (p+1 ,p+n+1 ,cmp); for (int i=1 ;i<=n;i++) {mp[p[i].b]=i;} for (int i=1 ;i<=n;i++) {nx[i]=mp[p[i].a],pre[mp[p[i].a]]=i;} ans=0 ,cur=1 ; while (1 ) { while (cur<=n&&p[cur].a==p[cur].b) {cur++;} if (cur==n+1 ) {break ;} int qian=cur,hou=cur; while (1 ) { qian=pre[qian]; if (chk (cur,qian)) {swp (cur,qian);break ;} hou=nx[hou]; if (chk (cur,hou)) {swp (cur,hou);break ;} } } int len=res.size (); printf ("%lld %d\n" ,ans,len); for (int i=0 ;i<len;i++) {printf ("%d %d\n" ,res[i].first,res[i].second);} res.clear (); } return 0 ; }
C. Cake 难度: $\bigstar$
题目大意:
给定 $2\times n$ 矩阵的初始状态和目标状态,每次操作可以 180 度翻转一个 $2\times 2$ 连续子矩阵,问至少需要几次操作,或报告无解。
$1\leq n\leq 5\times 10^5$
题目解法:
将所有偶数列上下翻转,问题转化为每次交换相邻,求逆序对即可。
代码
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 #include <bits/stdc++.h> #define ll long long using namespace std;const int N=500010 ;int t,n,a[N],b[N],c[N],fw[N],hd[N];ll ans; vector <int > v[N]; map <pair<int ,int >,int > mp; void modify (int x) { for (int i=x;i<=n;i+=(i&(-i))) {fw[i]++;} } int query (int x) { int res=0 ; for (int i=x;i;i-=(i&(-i))) {res+=fw[i];} return res; } int main () { scanf ("%d" ,&t); for (int ii=1 ;ii<=t;ii++) { mp.clear (); ans=0 ; scanf ("%d" ,&n); for (int i=1 ;i<=n;i++) {scanf ("%d" ,&a[i]);fw[i]=0 ;} int tot=0 ; for (int i=1 ;i<=n;i++) { scanf ("%d" ,&b[i]); if (i&1 ) {swap (a[i],b[i]);} if (mp.find (make_pair (a[i],b[i]))==mp.end ()) {mp[make_pair (a[i],b[i])]=++tot;hd[tot]=0 ;v[tot].clear ();} v[mp[make_pair (a[i],b[i])]].push_back (i); } for (int i=1 ;i<=n;i++) {scanf ("%d" ,&a[i]);} int flg=1 ; for (int i=1 ;i<=n;i++) { scanf ("%d" ,&b[i]); if (i&1 ) {swap (a[i],b[i]);} if (mp.find (make_pair (a[i],b[i]))==mp.end ()) {flg=0 ;continue ;} int tmp=mp[make_pair (a[i],b[i])]; if (hd[tmp]>=v[tmp].size ()) {flg=0 ;continue ;} c[i]=v[tmp][hd[tmp]++]; } if (!flg) {printf ("-1\n" );continue ;} for (int i=1 ;i<=n;i++) { ans+=i-1 -query (c[i]); modify (c[i]); } printf ("%lld\n" ,ans); } return 0 ; }
D. Divided Mechanism 难度: $\bigstar$
题目大意:
给定 $n\times m$ 网格,网格中有两个四连通块 $A,B$,接下来进行 $q$ 次操作,每次操作将 $B$ 向上下左右中的一个方向持续拉动,但不能与 $A$ 重叠,问最终能否使 $A,B$ 分离(使 $B$ 可自由移动)。
$n,m\leq 10, q\leq 100$
题目解法:
按题意模拟。
代码
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 #include <bits/stdc++.h> using namespace std;const int N=110 ;int t,n,m,tot,q,x[N],y[N],mp[N][N];char s[N];int chk (int x,int y) { if (x<=0 ||y<=0 ||x>n||y>m) {return 1 ;} return 1 -mp[x][y]; } int main () { scanf ("%d" ,&t); for (int ii=1 ;ii<=t;ii++) { tot=0 ; scanf ("%d%d%d" ,&n,&m,&q); for (int i=1 ;i<=n;i++) { scanf ("%s" ,s+1 ); for (int j=1 ;j<=m;j++) { mp[i][j]=(s[j]=='A' ); if (s[j]=='B' ) {++tot;x[tot]=i,y[tot]=j;} } } scanf ("%s" ,s+1 ); int flg=0 ; for (int i=1 ;i<=q;i++) { int dx=(s[i]=='N' ?-1 :(s[i]=='S' ?1 :0 )),dy=(s[i]=='W' ?-1 :(s[i]=='E' ?1 :0 )); for (int j=1 ;;j++) { if (j>=20 ) {flg=1 ;break ;} int flgg=1 ; for (int k=1 ;k<=tot;k++) { if (!chk (x[k]+dx,y[k]+dy)) {flgg=0 ;break ;} } if (!flgg) {break ;} for (int k=1 ;k<=tot;k++) {x[k]+=dx,y[k]+=dy;} } if (flg) {break ;} } if (flg) {printf ("TAK\n" );} else {printf ("NIE\n" );} } return 0 ; }
*E. Epidemic 难度: $\bigstar\bigstar\bigstar$
F. Fence 难度: $\bigstar$
题目大意:
给定长度为 $n$ 的序列 $a_{1\ldots n}$,对于每个 $1\leq x\leq \max a_i$,求出:
把每个 $a_i$ 拆分成 $x+x+\ldots+(a_i\bmod x)$ 后,新序列所有奇数项的和。
$1\leq \sum a_i\leq 10^6$
题目解法:
如果有连续一段 $a_i$ 满足 $a_i<x$,那么只需要这一段的奇数位和以及偶数位和即可,所以对于 $x$ 算答案时只需要 $x\leq a_i$ 的那些 $a_i$,按 $x$ 从小到大再利用双向链表即可做到 $O(\sum a_i)$。
代码
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 #include <bits/stdc++.h> using namespace std;const int N=1000010 ;int t,n,m,a[N],pre[N],nx[N],so[N],se[N];vector <int > v[N]; int main () { scanf ("%d" ,&t); for (int ii=1 ;ii<=t;ii++) { scanf ("%d" ,&n); m=0 ,nx[0 ]=1 ,pre[n+1 ]=n; for (int i=1 ;i<=n;i++) { pre[i]=i-1 ,nx[i]=i+1 ; scanf ("%d" ,&a[i]); m=max (m,a[i]); v[a[i]].push_back (i); so[i]=so[i-1 ]+a[i]*(i&1 ),se[i]=se[i-1 ]+a[i]*(!(i&1 )); } for (int i=1 ;i<=m;i++) { int ans=0 ,dq=0 ,cur=0 ; while (cur<=n) { if (cur) { int tmp=a[cur]/i; ans+=i*(dq==1 ?tmp/2 :(tmp+1 )/2 ); dq^=(tmp&1 ); if (a[cur]%i) { dq^=1 ; ans+=(a[cur]%i)*dq; } } int nt=nx[cur]-1 ; if (dq^(cur&1 )) {ans+=se[nt]-se[cur];} else {ans+=so[nt]-so[cur];} dq^=((nt-cur)&1 ); cur=nt+1 ; } printf ("%d\n" ,ans); int len=v[i].size (); for (int j=0 ;j<len;j++) { int y=pre[v[i][j]],z=nx[v[i][j]]; pre[z]=y,nx[y]=z; } } for (int i=1 ;i<=m;i++) {v[i].clear ();} } return 0 ; }
G. Gebyte’s Grind 难度: $\bigstar\bigstar$
题目大意:
给定函数序列 $f_{1\ldots n}$ 以及数 $h$,$f_i$ 有三种可能:
$f_i(x)=\max(0,x-v_i)$;
$f_i(x)=v_i\times [x\ge v_i]$;
$f_i(x)=\max(v_i,x)$。
特别地,总有 $f_i(0)=0$。
你需要支持 $q$ 次操作,操作有单点修改,以及给出 $x$ 查询最大的 $r$ 使得 $f_r(f_{r-1}(\ldots (f_x(h))))>0$。
$n,q\leq 2\times 10^6, v_i\ge 1$
题目解法:
注意到若干 $f_i$ 的复合总可以写成:
的形式(这里用了题解写的形式,实际上你也可能得到一些等价形式),于是线段树维护即可,时间复杂度 $O((n+q)\log n)$。
代码
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 #include <bits/stdc++.h> #define ll long long using namespace std;const int N=2000010 ;const ll INF=1e18 ;int cntt;struct P { ll a,b,v; P (ll x=0 ,ll y=0 ) { if (x==0 ) {a=b=v=0 ;} else if (x==1 ) {a=b=y,v=0 ;} else if (x==2 ) {a=y-1 ,v=y,b=1e18 ;} else if (x==3 ) {a=0 ,b=v=y;} } }seg[N*4 ]; P operator + (P a,P b) { P c; if (a.v<=b.a) { c.a=a.b+b.a-a.v; c.b=c.a+b.b-b.a; c.v=b.v; } else { c.a=a.a; if (a.v<=b.b) { c.b=a.b+b.b-a.v; c.v=b.v; } else { c.b=a.b; c.v=b.v+a.v-b.b; } } c.a=min (c.a,INF),c.b=min (c.b,INF),c.v=min (c.v,INF); return c; } ll read () { ll res=0 ; char c=getchar (); while (c<'0' ||c>'9' ) {c=getchar ();} while (c>='0' &&c<='9' ) { res=res*10 -'0' +c; c=getchar (); } return res; } void write (ll x) { if (x<=9 ) {putchar (x+'0' );return ;} write (x/10 ); putchar (x%10 +'0' ); } ll t,n,q,h,x,y; char s[10 ];void buildt (int p,int l,int r) { seg[p]=P (); if (l==r) {return ;} int mid=(l+r)>>1 ; buildt (p<<1 ,l,mid),buildt ((p<<1 )|1 ,mid+1 ,r); } void modify (int p,int l,int r,int pos,P v) { if (l==r) {seg[p]=v;return ;} int mid=(l+r)>>1 ; if (pos<=mid) {modify (p<<1 ,l,mid,pos,v);} else {modify ((p<<1 )|1 ,mid+1 ,r,pos,v);} seg[p]=seg[p<<1 ]+seg[(p<<1 )|1 ]; } pair<ll,P> query (int p,int l,int r,int x,P nw) { if (l==r) { if ((nw+seg[p]).a>=h) {return make_pair (l-1 ,nw);} else {return make_pair (l,nw+seg[p]);} } int mid=(l+r)>>1 ; if (x>mid) {return query ((p<<1 )|1 ,mid+1 ,r,x,nw);} else { pair<ll,P> tmp=query (p<<1 ,l,mid,x,nw); if (tmp.first<mid) {return tmp;} P tmpp=tmp.second+seg[(p<<1 )|1 ]; if (tmpp.a>=h) {return query ((p<<1 )|1 ,mid+1 ,r,x,tmp.second);} else {return make_pair (r,tmpp);} } } int main () { t=read (); for (int ii=1 ;ii<=t;ii++) { n=read ();q=read (),h=read (); buildt (1 ,1 ,n); for (int i=1 ;i<=n;i++) { scanf ("%s" ,s+1 ); x=read (); modify (1 ,1 ,n,i,P (s[1 ]=='B' ?1 :(s[1 ]=='K' ?2 :3 ),x)); } for (int i=1 ;i<=q;i++) { scanf ("%s" ,s+1 ); x=read (); if (s[1 ]=='Z' ) { scanf ("%s" ,s+1 ); y=read (); modify (1 ,1 ,n,x,P (s[1 ]=='B' ?1 :(s[1 ]=='K' ?2 :3 ),y)); } else { cntt++; ll res=query (1 ,1 ,n,x,P ()).first; if (res<x) { putchar ('-' ); putchar ('1' ); putchar ('\n' ); } else {write (res);putchar ('\n' );} } } } return 0 ; }
H. Hidden Password 难度: $\bigstar$
题目大意:
小写字符串 $H_1$ 通过每个字符加 $x$(超过 z 则回到 a) 得到 $H_2$,$H_2$ 通过每个字符加 $x$ 得到 $H_1$,且 $H_1\ne H_2$,给定 $H_1$ 求 $H_2$。
$|H_1|\leq 2\times 10^5$
题目解法:
每个字符加 $13$ 即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <bits/stdc++.h> using namespace std;const int N=200010 ;int t,n;char s[N];int main () { scanf ("%d" ,&t); for (int ii=1 ;ii<=t;ii++) { scanf ("%s" ,s+1 ); n=strlen (s+1 ); for (int i=1 ;i<=n;i++) {printf ("%c" ,'a' +(s[i]-'a' +13 )%26 );} printf ("\n" ); } return 0 ; }
I. Interesting Numbers 难度: $\bigstar\bigstar$
题目大意:
给定 $n$ 个数 $a_1,\ldots,a_n$,求最多能选出多少数使得两两的异或和 $\leq k$。
$n\leq 3\times 10^4, a_i<2^{20}$
题目解法:
经典的在 Trie 上 DFS,设 $dfs(x,y)$ 表示在深度相同的 $x,y$ 子树中各选出一些点满足条件的方案数,如果当前是第 $t$ 位,若 $k$ 的第 $t$ 位为 $0$,那么就只能递归到同向儿子,即 $\max(dfs(lc_x,lc_y),dfs(rc_x,rc_y))$(别忘了 $x,y$ 中有一个子树里一个点都没选的情况);否则可以递归到不同儿子,同时两个子问题独立,所以 $dfs(lc_x,rc_y)+dfs(rc_x,lc_y)$。
时间复杂度为 $O(n\log A)$。
代码
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;const int N=600010 ;int t,n,k,x,tot,ch[N][2 ],siz[N];void ins (int x) { int p=1 ; siz[p]++; for (int i=19 ;i>=0 ;i--) { int tmp=((x>>i)&1 ); if (!ch[p][tmp]) {ch[p][tmp]=++tot;} p=ch[p][tmp]; siz[p]++; } } int dfs (int x,int y,int d) { if (d==-1 ||!x||!y) {return siz[x]+siz[y];} int tmp=((k>>d)&1 ); if (tmp) {return dfs (ch[x][0 ],ch[y][1 ],d-1 )+dfs (ch[x][1 ],ch[y][0 ],d-1 );} else {return max (max (dfs (ch[x][0 ],ch[y][0 ],d-1 ),dfs (ch[x][1 ],ch[y][1 ],d-1 )),((1 <<d)>k)?0 :max (siz[x],siz[y]));} } int main () { scanf ("%d" ,&t); for (int ii=1 ;ii<=t;ii++) { scanf ("%d%d" ,&n,&k); tot=1 ; for (int i=1 ;i<=n;i++) { scanf ("%d" ,&x); ins (x); } printf ("%d\n" ,dfs (1 ,1 ,19 )/2 ); for (int i=1 ;i<=tot;i++) {siz[i]=ch[i][0 ]=ch[i][1 ]=0 ;} } return 0 ; }
J. Jungle Trail 难度: $\bigstar$
题目大意:
给定 $n\times m$ 网格,每个格子为空地,障碍,好蛇或坏蛇,你可以选择一些行和一些列,然后翻转这些行列上蛇的好坏状态(如果一行一列都被选,交点处负负得正),然后你需要找到一条从左上角到右下角的只经过空地和好蛇的只能向下向右走的路径,或报告无解。
$n,m\leq 2000$
题目解法:
随便找出一条不经过障碍的路径,然后每走到一个格子,如果这个格子是坏蛇就翻转一下新到的行或列即可(具体地,如果这一步往下走就翻转这一行,否则翻转这一列)。
代码
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 #include <bits/stdc++.h> using namespace std;const int N=2010 ;int t,n,m,r[N],c[N],ans[N*2 ],nx[N][N],mp[N][N];char s[N];int main () { scanf ("%d" ,&t); for (int ii=1 ;ii<=t;ii++) { scanf ("%d%d" ,&n,&m); for (int i=1 ;i<=n;i++) {r[i]=0 ;} for (int i=1 ;i<=m;i++) {c[i]=0 ;} for (int i=1 ;i<=n+m-2 ;i++) {ans[i]=0 ;} for (int i=1 ;i<=n;i++) { scanf ("%s" ,s+1 ); for (int j=1 ;j<=m;j++) {nx[i][j]=-1 ,mp[i][j]=(s[j]=='.' ?0 :(s[j]=='#' ?1 :(s[j]=='O' ?2 :3 )));} } for (int i=n;i>=1 ;i--) { for (int j=m;j>=1 ;j--) { if (i==n&&j==m) {nx[i][j]=0 ;} else if (mp[i][j]!=1 ) { if (i<n&&nx[i+1 ][j]>=0 ) {nx[i][j]=i+1 ;} if (j<m&&nx[i][j+1 ]>=0 ) {nx[i][j]=j+1 +n;} } } } if (nx[1 ][1 ]==-1 ) {printf ("NIE\n" );continue ;} int curx=1 ,cury=1 ,cnt=0 ; if (mp[1 ][1 ]==3 ) {r[1 ]=1 ;} while (curx!=n||cury!=m) { if (nx[curx][cury]<=n) { curx++; if ((mp[curx][cury]==3 &&!c[cury])||(mp[curx][cury]==2 &&c[cury])) {r[curx]=1 ;} ans[++cnt]=1 ; } else { cury++; if ((mp[curx][cury]==3 &&!r[curx])||(mp[curx][cury]==2 &&r[curx])) {c[cury]=1 ;} ans[++cnt]=0 ; } } printf ("TAK\n" ); for (int i=1 ;i<=n;i++) {printf ("%c" ,r[i]?'T' :'N' );} putchar ('\n' ); for (int i=1 ;i<=m;i++) {printf ("%c" ,c[i]?'T' :'N' );} putchar ('\n' ); for (int i=1 ;i<=cnt;i++) {printf ("%c" ,ans[i]?'D' :'P' );} putchar ('\n' ); } return 0 ; }
K. Kitten and Roomba 难度: $\bigstar$
题目大意:
给定一棵 $n$ 个点的树,猫猫一开始在点 $c$,机器人会通过(不一定简单的)树上路径 $a_{1\ldots n}$,如果机器人到达了猫猫所在的点,猫猫就会随机跑到一个邻居上,问猫猫期望会跑多少次。
$n\leq 10^6$
题目解法:
始终维护当前猫猫在每个点的概率 $p_i$,如果机器人到了 $x$,那么就把 $p_x$ 均匀分配给 $x$ 的邻居,在每个点上打标记维护其儿子的增量即可线性。
代码
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;const int N=1000010 ;int t,n,m,x,y,f[N],dgr[N];double ans,p[N],tg[N];vector <int > v[N]; int read () { int res=0 ; char c=getchar (); while (c<'0' ||c>'9' ) {c=getchar (); } while (c>='0' &&c<='9' ) { res=res*10 -'0' +c; c=getchar (); } return res; } void dfs (int x,int fa) { f[x]=fa; int len=v[x].size (); for (int i=0 ;i<len;i++) { if (v[x][i]==fa) {continue ;} dfs (v[x][i],x); } } int main () { t=read (); for (int ii=1 ;ii<=t;ii++) { n=read (); x=read (); for (int i=1 ;i<=n;i++) {p[i]=tg[i]=0 ,dgr[i]=0 ;v[i].clear ();} p[x]=1 ,ans=0 ; for (int i=1 ;i<=n-1 ;i++) { x=read ();y=read (); v[x].push_back (y),v[y].push_back (x); dgr[x]++,dgr[y]++; } dfs (1 ,0 ); m=read (); for (int i=1 ;i<=m;i++) { x=read (); double np=p[x]+tg[f[x]]; ans+=np,p[x]-=np,tg[x]+=np/(double )dgr[x],p[f[x]]+=np/(double )dgr[x]; } printf ("%.9f\n" ,ans); } return 0 ; }
L. Lemurs 难度: $\bigstar$
题目大意:
给定 $n\times m$ 网格以及数 $k$,网格中有点和叉,你需要选择一些叉,使得到某个叉的曼哈顿距离不超过 $k$ 的点集恰好是所有叉,或报告无解。
$n,m,k\leq 1000$
题目解法:
先从点开始 DFS,得到哪些叉可以被选,然后从这些叉 DFS,看看是不是覆盖了所有的叉,复杂度 $O(nm)$。
代码
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 #include <bits/stdc++.h> using namespace std;const int N=1010 ;int t,n,m,k,mp[N][N],dis1[N][N],dis2[N][N],dx[4 ]={0 ,0 ,1 ,-1 },dy[4 ]={1 ,-1 ,0 ,0 };queue < pair<int ,int > > q; char s[N];bool chk (int x,int y) {return (x>=1 &&y>=1 &&x<=n&&y<=m);}int main () { scanf ("%d" ,&t); for (int ii=1 ;ii<=t;ii++) { scanf ("%d%d%d" ,&n,&m,&k); for (int i=1 ;i<=n;i++) { scanf ("%s" ,s+1 ); for (int j=1 ;j<=m;j++) { if (s[j]=='.' ) {mp[i][j]=0 ,dis1[i][j]=0 ;q.push (make_pair (i,j));} else {mp[i][j]=1 ,dis1[i][j]=1e9 ;} dis2[i][j]=1e9 ; } } while (q.size ()) { pair<int ,int > a=q.front (); q.pop (); for (int i=0 ;i<=3 ;i++) { int tx=a.first+dx[i],ty=a.second+dy[i]; if (chk (tx,ty)&&dis1[tx][ty]==1e9 ) {dis1[tx][ty]=dis1[a.first][a.second]+1 ;q.push (make_pair (tx,ty));} } } for (int i=1 ;i<=n;i++) { for (int j=1 ;j<=m;j++) { if (dis1[i][j]>k) {dis2[i][j]=0 ;q.push (make_pair (i,j));} } } while (q.size ()) { pair<int ,int > a=q.front (); q.pop (); for (int i=0 ;i<=3 ;i++) { int tx=a.first+dx[i],ty=a.second+dy[i]; if (chk (tx,ty)&&dis2[tx][ty]==1e9 ) {dis2[tx][ty]=dis2[a.first][a.second]+1 ;q.push (make_pair (tx,ty));} } } int flg=1 ; for (int i=1 ;i<=n;i++) { for (int j=1 ;j<=m;j++) { if (mp[i][j]&&dis2[i][j]>k) {flg=0 ;break ;} } } if (!flg) {printf ("NIE\n" );} else {printf ("TAK\n" );} } return 0 ; }
难度: $\bigstar\bigstar\bigstar\bigstar$