atcoder beginer 348 (abc348) D E F 题解
E
非常经典的树上操作(树上DP)。父节点到某个子节点,值是如何变化的。
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <cmath> 5 #include <cstdbool> 6 #include <string> 7 #include <algorithm> 8 #include <iostream> 9 #include <sstream> 10 #include <ctime> 11 #include <stack> 12 #include <vector> 13 #include <queue> 14 #include <set> 15 #include <map> 16 #include <array> 17 using namespace std; 18 #define LL long long 19 #define ULL unsigned long long 20 21 const LL mod_1=1e9+7; 22 const LL mod_2=998244353; 23 24 const double eps_1=1e-5; 25 const double eps_2=1e-10; 26 27 const int maxn=1e5+10; 28 29 vector<LL> adj[maxn], child[maxn]; 30 LL n, c[maxn], fa[maxn], high[maxn] , value[maxn], st[maxn], result=5e18; 31 bool vis[maxn]; 32 33 void find_child(int d) 34 { 35 vis[d]=1; 36 st[d] = c[d]; 37 for (int chi:adj[d]) 38 if (!vis[chi]) 39 { 40 fa[chi]=d; 41 child[d].push_back(chi); 42 high[chi]=high[d]+1; 43 44 find_child(chi); 45 46 st[d] += st[chi]; 47 } 48 } 49 50 void solve(int d) 51 { 52 if (d!=1) 53 value[d] = value[ fa[d] ] - st[d] + (st[1]-st[d]); 54 55 //value[d] = value[ fa[d] ] - ( c[ fa[d] ] * st[d] ) + ( c[d] * (c[1]-st[d]) ); 56 for (int chi:child[d]) 57 solve(chi); 58 } 59 60 int main() 61 { 62 LL a,b,i; 63 cin>>n; 64 for (i=1;i<n;i++) 65 { 66 cin>>a>>b; 67 adj[a].push_back(b); 68 adj[b].push_back(a); 69 } 70 for (i=1;i<=n;i++) 71 cin>>c[i]; 72 73 high[1]=0; 74 find_child(1); 75 76 memset(value,0,sizeof(value)); 77 value[1]=0; 78 for (i=1;i<=n;i++) 79 value[1]+=high[i]*c[i]; 80 81 solve(1); 82 83 for (i=1;i<=n;i++) 84 result=min(result, value[i]); 85 86 cout<<result; 87 88 return 0; 89 }
D
感觉做的时候有点晕。
写了两种方法。
第一种:
一开始是dfs,x,y坐标,记录当前这个位置可以达到的最大E值(energy),若遍历得到的新的E值没有比现在的好,就不做遍历。最坏时间复杂度是h*w*E E=h*w,这样看的话是要超时的
我还添加了T点(终点)到所有点的最短距离,如果到达某一点后,可以直接到达T点,那么输出Yes结束。如果用dfs和这个,还是超时。
但是这样超时,于是dfs改为优先队列,创建E值从大到小的堆。
所有样例6ms内就解决了,让人很诧异。大概是因为有了【若遍历得到的新的E值没有比现在的好,就不做遍历】的约束,再加上【优先队列保证E值优先跑最大的】,所有基本上跑的都是最优方案,绝大部分次一点的方案没有跑。
然后有了这个后,前面的那个T点距离优化可以不用加,不加的话,运行时间从6ms变为8ms。
第二种:
S、T点(起点、终点)和有药物的点,它们可以用bfs的方式去处理。
从T点(终点)倒推。
如果现在在U点,前面已经证实了可以从U点到T点(终点)。如果U点倒推到下一个点R点,那么R点也可以到T点。U点倒推到下一个点R点,前提是U点到R点的距离,要小于等于R点有的药物值,这样的话,R点可以走到T点。
然后S、T点(起点、终点)和有药物的点,它们两两求它们之间的距离,同样也是bfs去处理。这个的时间复杂度是O(n^2*h*w)。
有点那种点压缩的感觉,就像tarjan分为若干个子块一样。
Sol1
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <cmath> 5 #include <cstdbool> 6 #include <string> 7 #include <algorithm> 8 #include <iostream> 9 #include <sstream> 10 #include <ctime> 11 #include <stack> 12 #include <vector> 13 #include <queue> 14 #include <set> 15 #include <map> 16 #include <array> 17 #include <bitset> 18 using namespace std; 19 #define LL long long 20 #define ULL unsigned long long 21 22 const LL mod_1=1e9+7; 23 const LL mod_2=998244353; 24 25 const double eps_1=1e-5; 26 const double eps_2=1e-10; 27 28 const int maxn=2e2+10; 29 30 bool stop[maxn][maxn]; 31 32 int maxE[maxn][maxn]; 33 34 int med[maxn][maxn]; 35 36 int dx[4]={0,0,1,-1}; 37 int dy[4]={1,-1,0,0}; 38 int row,col,tx,ty; 39 40 41 bool use[maxn][maxn]; 42 int qx[maxn*maxn], qy[maxn*maxn], step[maxn][maxn]; 43 bool judge[maxn][maxn]; 44 45 priority_queue< array<int, 3> > que; 46 47 48 ///siz=row*col O(siz*E) = 1600000000 49 ///有没有方法证明这个方法复杂度会低一点/很多? 50 ///AC 49 ; TLE 4 51 52 void dfs(int x, int y) 53 { 54 int i,xx,yy; 55 ///====== 56 57 /** 58 1. 还是4TLE 59 2. 先往终点/step[x][y]小的方向走? 60 3. 按照E的能量排序走? 61 4. 优先队列? 62 **/ 63 64 if (x==tx && y==ty) 65 { 66 cout<<"Yes"<<endl; 67 exit(0); 68 } 69 if (maxE[x][y]>=step[x][y]) 70 { 71 cout<<"Yes"<<endl; 72 exit(0); 73 } 74 75 ///====== 76 77 for (i=0;i<4;i++) 78 { 79 xx=x+dx[i]; 80 yy=y+dy[i]; 81 82 if (xx>=0 && xx<row && yy>=0 && yy<col && stop[xx][yy]==0 && maxE[x][y]-1>maxE[xx][yy]) 83 { 84 maxE[xx][yy]=max(maxE[x][y]-1, med[xx][yy]); 85 dfs(xx,yy); 86 } 87 } 88 } 89 90 int main() 91 { 92 int i,j; 93 int q,sx,sy,x,y,e; 94 char ch; 95 int head,tail,xx,yy; 96 memset(stop,0,sizeof(stop)); 97 scanf("%d%d%c",&row,&col,&ch); 98 for (i=0;i<row;i++) 99 { 100 for (j=0;j<col;j++) 101 { 102 scanf("%c",&ch); 103 if (ch=='#') 104 stop[i][j]=1; 105 else if (ch=='S') 106 sx=i, sy=j; 107 else if (ch=='T') 108 tx=i, ty=j; 109 } 110 scanf("%c",&ch); 111 } 112 113 memset(med,0,sizeof(med)); 114 scanf("%d",&q); 115 while (q--) 116 { 117 scanf("%d%d%d",&x,&y,&e); 118 x--, y--; 119 med[x][y]=e; 120 } 121 122 123 memset(use,0,sizeof(use)); 124 memset(step,127 ,sizeof(step)); 125 head=0, tail=1; 126 qx[1]=tx, qy[1]=ty, use[tx][ty]=1, step[tx][ty]=0; 127 while (head<tail) 128 { 129 head++; 130 x=qx[head], y=qy[head]; 131 132 for (i=0;i<4;i++) 133 { 134 xx=x+dx[i]; 135 yy=y+dy[i]; 136 137 if (xx>=0 && xx<row && yy>=0 && yy<col && stop[xx][yy]==0 && !use[xx][yy]) 138 { 139 tail++; 140 qx[tail]=xx, qy[tail]=yy, use[xx][yy]=1, step[xx][yy]=step[x][y]+1; 141 } 142 } 143 } 144 /* 145 for (i=0;i<row;i++) 146 { 147 for (j=0;j<col;j++) 148 printf("%d ",step[i][j]); 149 printf("\n"); 150 } 151 */ 152 153 memset(maxE,0xff,sizeof(maxE)); 154 maxE[sx][sy]=med[sx][sy]; 155 memset(judge,0,sizeof(judge)); 156 //dfs(sx,sy); 157 158 ///* 159 que.push({med[sx][sy],sx,sy}); 160 while (!que.empty()) 161 { 162 auto temp=que.top(); 163 que.pop(); 164 e=temp[0], x=temp[1], y=temp[2]; 165 if (maxE[x][y]>e) 166 continue; 167 168 //maxE[x][y]=e; 169 ///judge[x][y]=1; 170 //cout<<"test "<<e<<" "<<x<<" "<<y<<endl; 171 172 if (x==tx && y==ty) 173 { 174 cout<<"Yes"<<endl; 175 exit(0); 176 } 177 if (maxE[x][y]>=step[x][y]) 178 { 179 cout<<"Yes"<<endl; 180 exit(0); 181 } 182 183 for (i=0;i<4;i++) 184 { 185 xx=x+dx[i]; 186 yy=y+dy[i]; 187 188 if (xx>=0 && xx<row && yy>=0 && yy<col && stop[xx][yy]==0 && e-1>maxE[xx][yy]) /// && !judge[xx][yy] 189 { 190 //maxE[xx][yy]=e-1; 191 maxE[xx][yy]=max(e-1, med[xx][yy]); 192 que.push({maxE[xx][yy],xx,yy}); 193 } 194 } 195 } 196 //*/ 197 198 ///药物只能吃一次:其实最多只需要吃一次。这个条件就可以去掉 199 200 201 cout<<"No"<<endl; 202 203 return 0; 204 } 205 /* 206 6 6 207 ...#.# 208 ....S# 209 ...... 210 ####.# 211 ####.# 212 ####T# 213 1 214 2 5 4 215 216 ====== 217 218 6 6 219 ...#.# 220 ....S# 221 ...... 222 ####.# 223 ####.# 224 ####T# 225 1 226 2 5 3 227 228 ====== 229 230 6 6 231 ...#.# 232 ....S# 233 ....#. 234 ####.# 235 ####.# 236 ####T# 237 1 238 2 5 4 239 240 ====== 241 242 243 */
Sol2
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <cmath> 5 #include <cstdbool> 6 #include <string> 7 #include <algorithm> 8 #include <iostream> 9 #include <sstream> 10 #include <ctime> 11 #include <stack> 12 #include <vector> 13 #include <queue> 14 #include <set> 15 #include <map> 16 #include <array> 17 #include <bitset> 18 using namespace std; 19 #define LL long long 20 #define ULL unsigned long long 21 22 const LL mod_1=1e9+7; 23 const LL mod_2=998244353; 24 25 const double eps_1=1e-5; 26 const double eps_2=1e-10; 27 28 const int maxn=2e2+10; 29 const int maxg=maxn*maxn; 30 const int maxz=3e2+10; ///maxzhongzhuan 31 32 bool stop[maxn][maxn]; 33 34 int maxE[maxn][maxn]; 35 36 int med[maxn][maxn]; 37 38 int dx[4]={0,0,1,-1}; 39 int dy[4]={1,-1,0,0}; 40 int row,col,sx,sy,tx,ty; 41 42 bool use[maxn][maxn]; 43 int qx[maxg], qy[maxg], step[maxn][maxn]; 44 int cx[maxz],cy[maxz]; 45 int dist[maxz][maxz]; 46 //int zx[maxz],zy[maxz]; 47 int qindex[maxz]; 48 bool judge[maxz]; 49 50 51 int main() 52 { 53 int i,j; 54 int q,x,y,e; 55 char ch; 56 int head,tail,xx,yy; 57 int index; 58 59 memset(stop,0,sizeof(stop)); 60 scanf("%d%d%c",&row,&col,&ch); 61 for (i=0;i<row;i++) 62 { 63 for (j=0;j<col;j++) 64 { 65 scanf("%c",&ch); 66 if (ch=='#') 67 stop[i][j]=1; 68 else if (ch=='S') 69 sx=i, sy=j; 70 else if (ch=='T') 71 tx=i, ty=j; 72 } 73 scanf("%c",&ch); 74 } 75 76 memset(med,0,sizeof(med)); 77 scanf("%d",&q); 78 for (i=1;i<=q;i++) 79 { 80 scanf("%d%d%d",&x,&y,&e); 81 x--, y--; 82 cx[i]=x, cy[i]=y; 83 med[x][y]=e; 84 } 85 86 ///====== 87 88 cx[q+1]=sx, cy[q+1]=sy; 89 cx[q+2]=tx, cy[q+2]=ty; 90 q+=2; 91 92 for (j=1;j<=q;j++) 93 { 94 memset(use,0,sizeof(use)); 95 memset(step,127 ,sizeof(step)); 96 head=0, tail=1; 97 qx[1]=cx[j], qy[1]=cy[j], use[qx[1]][qy[1]]=1, step[qx[1]][qy[1]]=0; 98 while (head<tail) 99 { 100 head++; 101 x=qx[head], y=qy[head]; 102 103 for (i=0;i<4;i++) 104 { 105 xx=x+dx[i]; 106 yy=y+dy[i]; 107 108 if (xx>=0 && xx<row && yy>=0 && yy<col && stop[xx][yy]==0 && !use[xx][yy]) 109 { 110 tail++; 111 qx[tail]=xx, qy[tail]=yy, use[xx][yy]=1, step[xx][yy]=step[x][y]+1; 112 } 113 } 114 } 115 for (i=1;i<=q;i++) 116 dist[j][i]=step[ cx[i] ][ cy[i] ]; 117 } 118 119 120 head=0, tail=1; 121 memset(judge,0,sizeof(judge)); 122 qindex[1]=q, judge[ qindex[1] ]=1; ///q === t 123 while (head<tail) 124 { 125 head++; 126 index=qindex[head]; 127 128 if (index==q-1) ///q-1 == s 129 { 130 cout<<"Yes"<<endl; 131 exit(0); 132 } 133 134 for (i=1;i<=q;i++) 135 if (i!=index && !judge[i] && dist[index][i] <= med[ cx[i] ][ cy[i] ]) 136 { 137 tail++; 138 qindex[tail]=i, judge[i]=1; 139 } 140 } 141 142 cout<<"No"<<endl; 143 144 return 0; 145 }
F
就很有bitset的感觉。
本来是n*n/2*m的复杂度,直接暴力的话,但是这样超时。
但是没有除了暴力以外的方法。题目处理的是两个序列的比较,序列可以转化为bitset,可以时间和空间复杂度都能降低。
记录第j列,选取的数值为a[i][j]时,这个列(和其它列是单独孤立的)当选择数值为a[i][j],所有序列和它是否相同。如果这一列是 3 4 5 4 2,如果a[i][j]=3,那么它是10000,如果a[i][j]=4,那么它是01010。
问题转化为一个序列,和其它序列的bitset处理。
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cstring> 4 #include <cmath> 5 #include <cstdbool> 6 #include <string> 7 #include <algorithm> 8 #include <iostream> 9 #include <sstream> 10 #include <ctime> 11 #include <stack> 12 #include <vector> 13 #include <queue> 14 #include <set> 15 #include <map> 16 #include <array> 17 #include <bitset> 18 using namespace std; 19 #define LL long long 20 #define ULL unsigned long long 21 22 const LL mod_1=1e9+7; 23 const LL mod_2=998244353; 24 25 const double eps_1=1e-5; 26 const double eps_2=1e-10; 27 28 const int maxn=2e3+10; 29 const int maxm=2e3+10; 30 const int maxa=1e3+10; 31 32 bitset<maxn> bs[maxm][maxa]; 33 int a[maxn][maxm]; 34 bitset<maxn> cur; 35 36 int main() 37 { 38 int n,m,i,j,cnt=0; 39 40 scanf("%d%d",&n,&m); 41 for (i=0;i<n;i++) 42 { 43 for (j=0;j<m;j++) 44 { 45 scanf("%d",&a[i][j]); 46 bs[j][ a[i][j] ][i]=1; 47 } 48 } 49 50 for (i=0;i<n;i++) 51 { 52 cur.reset(); 53 for (j=0;j<m;j++) 54 cur=cur ^ bs[j][ a[i][j] ]; ///xor 55 56 /* 57 cout<<"test i="<<i<<" "; 58 for (j=0;j<n;j++) 59 cout<<cur[j]; 60 cout<<endl; 61 */ 62 63 cnt += cur.count(); 64 } 65 66 if (m & 1) 67 cnt-=n; 68 69 printf("%d",cnt/2); 70 71 return 0; 72 } 73 /* 74 4 4 75 1 1 1 1 76 1 1 1 1 77 1 1 1 1 78 1 1 1 1 79 80 ====== 81 82 5 4 83 1 1 1 1 84 1 1 1 1 85 1 1 1 1 86 1 1 1 1 87 1 1 1 1 88 89 ====== 90 91 5 5 92 1 1 1 1 1 93 1 1 1 1 1 94 1 1 1 1 1 95 1 1 1 1 1 96 1 1 1 1 1 97 98 ====== 99 100 101 102 */
也可以参考 AtCoder Beginner Contest 348 A 至 F 題讲解 by dreamoon_哔哩哔哩_bilibili