์ด ๋ฌธ์๋ ํ ๋น์ ์คํ๋ง 3.1 ์ธํธ ์ฑ ์ ์๊ฐ๋๋ ๊ฐ๋ ๋ค์ ์ ๋ฆฌํ ๋ฌธ์์ ๋๋ค.
โํญ์ ํ๋ ์์ํฌ ๊ธฐ๋ฐ์ ์ ๊ทผ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ผโ ๋ผ๋ ๊ฒ์ OOP์ ์ฅ์ ์ ์ต๋ํ ์ด๋ฆฐ ๊ฒ์ด ์คํ๋ง์ด๊ณ , ์ด๊ฒ์ ๊ธฐ๋ฐ์ผ๋กํ ์ ๊ทผ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ฉด ์คํ๋ง์ด ์ถ๊ตฌํ๋ ์ํคํ ์ฒ, ์ค๊ณ์ ๊ทผ๊ฐ์ด ํ๋ค๋ฆด ์ผ ์์ด ํ์ฅ๋์ด ๊ฒฐ๊ตญ์ ํ์ฅ๋ ์ฝ๋๋ง์ ๋ ์คํ๋ง๊ณผ ๊ฐ์ด ํ์ฅ์ฑ์ด ๋ฐ์ด๋๊ณ ์ ์ง๋ณด์๊ฐ ์ฌ์ด ์ฝ๋๊ฐ ๋๊ธฐ ๋๋ฌธ
DAO
Data Access Object : DB๋ฅผ ์ฌ์ฉํด ๋ฐ์ดํฐ๋ฅผ ์กฐํ, ์กฐ์ํ๋ ๊ธฐ๋ฅ์ ์ ๋ฌํ๋ ๊ฐ์ฒด
JavaBean
์๋๋ ๋น์ฃผ์ผ ํด์์ ์กฐ์ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ, ๊ทธ๋ฌ๋ ํ์ฌ์๋ ๋ค์ 2๊ฐ์ง ๊ด๋ก๋ฅผ ๋ฐ๋ผ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ฅผ ์ง์นญ
- Default Constructor์ ๊ฐ๊ณ ์์ (ํด/ํ๋ ์์ํฌ์์ Reflection์ ํตํด ์ค๋ธ์ ํธ๋ฅผ ์์ฑํ๊ธฐ ๋๋ฌธ)
- Property: Setter, Getter์ ์ด์ฉํด ์์ /์กฐํํ ์ ์์
DAO์์ ๋ค์๊ณผ ๊ฐ์ ์ผ๋ค์ ํ ๋ฉ์๋ ์์์ ์ ๋ถ ์ํค๋ฉด ์ด๋ค ๋ฌธ์ ์ ์ด ๋ฐ์ํ ๊น?
๋ง์ฝ DB ๊ณ์ ์ id/pw๋ฅผ ๋ฐ๊พธ๋ ๋ฑ ์ฌ์ํ ์์ ์ฌํญ์ด ์๊ธฐ๋ ๊ฒฝ์ฐ์๋ ์ ์ฒด ์ฝ๋์ ์ฌ๋ฌ ๊ณณ์ ์ฐ๋ฐ์ ์ผ๋ก ์์ ํด์ผ ํ๋ ๋ฌธ์ ๊ฐ ์๊ธด๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ๋ฅผ ํด์ผ ํ๋ค.
๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ (Sepration of Concerns)
๊ด์ฌ์ด ๊ฐ์ ๊ฒ๋ผ๋ฆฌ๋ ํ๋์ ๊ฐ์ฒด ํน์ ์นํ ๊ฐ์ฒด๋ก ๋ชจ์ด๊ฒ ํ๊ณ , ๊ด์ฌ์ด ๋ค๋ฅธ ๊ฒ๋ผ๋ฆฌ๋ ๊ฐ๋ฅํ ๋จ์ด๋จ๋ ค ์ํฅ์ ๋ผ์น์ง ์๊ฒ ๋ง๋๋ ๊ฒ.
์ด ๊ฒฝ์ฐ Connection์ ์ป์ด์ค๋ ๋ถ๋ถ์ ๋ค๋ฅธ ๋ฉ์๋๋ก ๋นผ๋ ๋ฐฉ์์ผ๋ก ๋ฆฌํฉํ ๋ง์ ํ๋ฉด ์์์ ๋งํ ๋ฌธ์ ๋ ํด๊ฒฐ๋๋ค.
๋ฉ์๋ ์ถ์ถ (Extract Method)
๊ณตํต ๊ธฐ๋ฅ์ ๊ทธ๊ฒ์ ๋ด๋นํ๋ ๋ฉ์๋๋ก ์ค๋ณต๋ ์ฝ๋๋ฅผ ๋ฝ์๋ด๋ ๊ฒ
์์ ๊ฐ์ด ๋ฉ์๋ ์ถ์ถ์ ํ๋ฉด, ์ด๋ ์ ๋์ ๋ฌธ์ ๋ ํด๊ฒฐ๋๋ค. ํ์ง๋ง ์ฌ์ฉํ๋ DB ์์ฒด๋ฅผ ๋ฐ๊ฟ ๊ฒฝ์ฐ์๋ ์ด๋ป๊ฒ ํ ๊ฒ์ธ๊ฐ?
๋ฌผ๋ก getConnection()
๋ฉ์๋๋ฅผ ์กฐ๊ธ๋ง ์์ ํ๋ฉด ๋๋ ์ผ์ด์ง๋ง, UserDao ์์ฒด๋ ํด๋์ค ๋ฐ์ด๋๋ฆฌ ํ์ผ๋ก ์ฌ์ฉํ๊ณ ์ถ์ผ๋ฉด ์์ค ์์ฒด๋ฅผ ๊ณ ์น ์๋ ์๋ ์ผ์ด๋ค.
์ด ๊ฒฝ์ฐ getConnection()
์ abstract method๋ก ์์ ํ๊ณ , ๊ทธ ๊ตฌํ๋ถ๋ฅผ ์์์ ๋ฐ์ ๊ตฌํํ๋ ์์ผ๋ก ๋ฆฌํฉํ ๋ง ํ ์ ์๋ค.
ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด (Template Method Pattern)
์ํผํด๋์ค์์ ๊ธฐ๋ฅ์ ์ผ๋ถ๋ฅผ abstract method ํน์ overriding ๊ฐ๋ฅํ protected method๋ก ๋ง๋ ๋ค ์๋ธํด๋์ค์์ ํ์์ ๋ฐ๋ผ ๋ฉ์๋๋ฅผ ๊ตฌํํ๋๋ก ํ๋ ํจํด
- abstract method : ์๋ธ ํด๋์ค์์ ๋ฐ๋์ ๊ตฌํํด์ผ ํ๋ ๊ธฐ๋ฅ
- hook method : ์๋ธ ํด๋์ค์์ overriding์ ํตํด ํ์ฅ์ ํ ์ ์๋ ๊ธฐ๋ฅ
์ด ๋ Dao
์ getConnection()
๋ฉ์๋๋ ์ด๋ค Connection ํด๋์ค๋ฅผ ์์ฑํ ์ง ๊ฒฐ์ ํ๋ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ๋ ๋ณผ ์ ์๊ณ , ์ด๋ฅผ ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด์ด๋ผ๊ณ ํ๋ค.
ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด (Factory Method Pattern)
ํ ํ๋ฆฟ ๋ฉ์๋ ํจํด๊ณผ ์๋ฆฌ๋ ๊ฐ์ผ๋, ๊ตฌ์ฒด์ ์ธ ์ค๋ธ์ ํธ ์์ฑ ๋ฐฉ๋ฒ์ ๋ํด ๊ฒฐ์ ํ๊ฒ ํ๋ ๊ฒ. ์ฃผ๋ก Interface ํ์ ์ค๋ธ์ ํธ๋ฅผ ๋ฐํํ๋ฏ๋ก ์๋ธ ํด๋์ค๋ ๋ค์ํ ๋ฐฉ๋ฒ์ผ๋ก ์ค๋ธ์ ํธ๋ฅผ ์์ฑํ๋ ๋ฉ์๋๋ฅผ ์์ฑ ํ ์ ์๋ค. ์ด ๋ ์ค๋ธ์ ํธ ์์ฑ ๋ฐฉ๋ฒ๊ณผ ํด๋์ค๋ฅผ ๊ฒฐ์ ํ ์ ์๋๋ก ๋ฏธ๋ฆฌ ์ ์ํด๋ ๋ฉ์๋๋ฅผ ํฉํ ๋ฆฌ ๋ฉ์๋๋ผ๊ณ ํ๋ค.
์๋ฐ์์๋ ์ด๋ค ์ค๋ธ์ ํธ๋ฅผ ์์ฑํ๋ ๋ฉ์๋๋ฅผ ์ผ๋ฐ์ ์ผ๋ก ํฉํ ๋ฆฌ ๋ฉ์๋๋ผ๊ณ ํ๋๋ฐ, ์ด๊ฒ์ ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด์ ํฉํ ๋ฆฌ ๋ฉ์๋์ ํท๊ฐ๋ฆฌ๋ฉด ์ ๋๋ค.
๋ ๊ฐ์ง ๊ด์ฌ์ ์ํ์ ํด๋์ค๋ก ๋ถ๋ฆฌ์ํค๋๋ฐ๋ ์ฑ๊ณต์ ํ์ง๋ง, ์ฌ์ ํ ๋ฌธ์ ๊ฐ ๋จ์์๋ค.
Dao
๊ฐ์ฒด ์์ฒด์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ฅผ ๋ง๋ค์ด๋ฒ๋ฆฌ๋ฉด ๋ค๋ฅธ ๋ชฉ์ ์ผ๋ก Dao
๋ฅผ ์์์ํค์ง ๋ชปํ๋ค๋ ๋จ์ ์ด ์๋ค. ๋ํ ์์์ ํตํ ์ํ์ ํด๋์ค ๊ด๊ณ๋ ๊ธด๋ฐํ๊ฒ ๊ฒฐํฉ๋๋ค ๊ฒ๋ ๋ฌธ์ ์ด๋ค. ๋ถ๋ช
Connection๋ง์ ๊ฐ์ง๊ณ ์ค๊ณ ์ถ์์ ๋ฟ์ธ๋ฐ, ๋ถํ์ํ ๊ฒฐํฉ์ด ์๊ฒจ๋ฒ๋ฆฌ๋ ๊ฒ์ด๋ค. ๋ง์ง๋ง์ผ๋ก ์ด Dao
๊ฐ์ฒด ๋ฟ ์๋๋ผ ๋ค๋ฅธ Dao
๊ฐ์ฒด๋ getConnection()
์ด ํ์ํ๋ฐ, ์ด ๋๋ฌธ์ ๋ ์ค๋ณต๋๋ ์ฝ๋๊ฐ ์๊ธด๋ค.
์ด๋ฅผ ํด๊ฒฐํ๋ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ด ์๋ค. ConnectionMaker
์ ๊ฐ์ด ์์ DB Connection์ ์ป์ด์ค๋ ์ ์ฉ ๊ฐ์ฒด๋ฅผ ๋ฐ๋ก ๋ง๋๋ ๊ฒ์ด๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ค์ค ์์ ๋ฌธ์ ์ ์ฝ๋ ์ฌ์ฌ์ฉ ๋ฌธ์ ๊ฐ ๋์์ ํด๊ฒฐ๋๋ค.
๋จ, ConnectionMaker
๋ฅผ ๊ตฌํํ ๋ getConnection()
๋ฉ์๋์ ์ง์ ๊ตฌํ์ ๋๋ ค๋ฐ๋๋ค๋ฉด Dao
๊ฐ์ฒด์ ์ง์ ๊ตฌํํ์ ๋์ ๋ง์ฐฌ๊ฐ์ง์ ๋ฌธ์ ์ ์ด ์๊ธด๋ค. ๋ฐ๋ผ์ ๋น์ฐํ ConnectionMaker
๋ ์ธํฐํ์ด์ค๋ก ์ ์ธ๋ผ์ผํ๊ณ , ์ง์ ์ ์ธ ๊ตฌํ๋ถ๋ ๊ทธ๊ฒ์ ์๋ธํด๋์ค์ ๋งก๊ฒจ์ผํ๋ค. ์ฆ, Dao
์๋ ์ธํฐํ์ด์ค๋ก์ ConnectionMaker
๊ฐ ์ถ์ํ๋์ด ๋ณ์๋ก ์ ์ธ๋์ด ์์ด ๊ทธ ๊ตฌํ๋ถ๊ฐ ๋ฐ๋์ด๋ ์ ๊ฒฝ ์ธ ์ผ์ด ์์ด์ง๋ค.
ํ์ง๋ง, ์ ์ธ์ ์ธํฐํ์ด์ค์ธ ConnectionMaker
๋ก ํ๋๋ผ๋, Dao
์์์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ๊ฐ์ฒด๋ฅผ ์ง์ ์์ฑํ๋ค๋ฉด ์ฌ์ ํ ๋ฌธ์ ๋ ์กด์ฌํ๋ค! ์ฆ, Dao
์์ new MyConnectionMaker()
๋ผ๋ ๋ฌธ์ฅ์ด ์๋ ํ ์ฌ์ ํ ๊ตฌํ๋ถ์ ๋ฐ๋ผ Dao
๋ ๋ฐ๋์ด์ผ ํ ๊ฒ์ด๋ค.
์ฌํ๊น์ง Connection์ ๋ํ ๊ด์ฌ์ ๋ค๋ฅธ ํด๋์ค๋ก ๋ชจ๋ ๋บ ๊ฒ์ฒ๋ผ ๋ณด์ด์ง๋ง, ์ฌ์ ํ Dao
์์๋ Connection์ ๋ํ ๊ด์ฌ์ด ์กด์ฌํ๊ธฐ ๋๋ฌธ์ ์ด๋ฐ ํ์์ด ๋ฐ์ํ๋ค. new MyConnectionMaker()
์์ฒด๋ ์งง๊ธฐ๋ง, ๊ทธ ์์ฒด์ MyConnectionMaker
์ ์ฌ์ฉํ์ฌ ์ฐ๊ฒฐํ๋ค๋ ๊ด์ฌ์ด ํจ์ถ๋์ด ์๋ ๊ฒ์ด๋ค. ์ด๊ฒ์ ์ฆ, Dao
์ ConnectionMaker
๋ก๋ MyConnectionMaker
์ ์ด๋ค๋ ๊ด๊ณ ์ค์ ์ ๋ํ ๊ด์ฌ์ด๋ค.
์ด๋ฐ ๊ด๊ณ ์ค์ ์ ์ค์ ๋ก Dao
๋ฅผ ์ฌ์ฉํ๋ Client Object์์ ํ๋๋ก ๋ถ๋ฆฌํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ข๋ค. connectionMaker = new MyConnectionMaker();
์ด๋ผ๋ ์ฝ๋๋ MyConnectionMaker
๊ฐ์ฒด์ ๋ ํผ๋ฐ์ค๋ฅผ connectionMaker
์ด๋ผ๋ ๋ณ์์ ๋ฃ์ด ์ฌ์ฉํ๊ฒ ํจ์ผ๋ก์จ ์ด ๋ ๊ฐ์ ๊ฐ์ฒด๊ฐ โ์ฌ์ฉโ์ด๋ผ๋ ๊ด๊ณ๋ฅผ ๋งบ๋๋ก ํ๋ ๊ฒ์ด๋ค!
์์ฝํ์๋ฉด, ๊ฐ์ฒด์ Dynamic Type์ Dao
๋ฅผ ์ค์ ๋ก ์ฌ์ฉํ๋ ์ฝ๋(ํด๋ผ์ด์ธํธ)์์ ์ ํ์ฌ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ , ๊ทธ๊ฒ์ Dao
์๊ฒ ํ๋ผ๋ฏธํฐ ๋ฑ์ ์๋จ์ผ๋ก ์ ๋ฌํ์ฌ ๊ด๊ณ๋ฅผ ์ฐ๊ฒฐํด์ฃผ๋ฉด ๋๋ค. (๋คํ์ฑ = Polymorphism)
ํด๋์ค ์ฌ์ด์ ๋ง๋ค์ด์ง๋ ๊ด๊ณ : ํด๋์ค ์ฝ๋์ ๋ค๋ฅธ ํด๋์ค ์ด๋ฆ์ด ๋ํ๋๊ธฐ ๋๋ฌธ์ ๋งบ์ด์ง๋ ๊ด๊ณ
๊ฐ์ฒด ์ฌ์ด์ ๋ง๋ค์ด์ง๋ ๊ด๊ณ : Runtime์์ ๊ฐ์ฒด์ Static Type๊ณผ Dynamic Type ์ฌ์ด์ ๋งบ์ด์ง๋ ๊ด๊ณ
์ด๋ก์จ Dao
๊ฐ์ฒด๋ ๋ณธ๋์ ๊ด์ฌ์ฌ์ธ ๋ฐ์ดํฐ ์ก์ธ์ค๋ฅผ ์ํ SQL ์์ฑ, ์คํ์ ์ง์คํ ์ ์๊ฒ ๋๊ณ Connection์ ์ค์ ํ๋ ๋ถ๋ถ์ ConnectionMaker
์, ConnectionMaker
์ ๊ด๊ณ ์ค์ ์ ํ๋ ๋ถ๋ถ์ ํด๋ผ์ด์ธํธ์ ๋ ๋๊ธธ ์ ์๊ฒ ๋๋ค.
์์ฝํ์๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ณผ์ ์ ํตํด ๋ฆฌํฉํ ๋ง์ ํ์๋ค.
Dao
๋ฉ์๋์ ๋๋ ค๋ฐ๋๋ค.Dao
์์ฒด์ ํฉํ ๋ฆฌ ๋ฉ์๋ ํจํด์ ์ ์ฉํ์ฌ ์ถ์ํ๋ฅผ ํตํ ํ์ฅ์ฑ์ ๋์ธ๋ค.Dao
์ ๋ค๋ฅธ ๋ชฉ์ ์ ์ํ ์์ ๋ฌธ์ ํด๊ฒฐ์ ์ํด ํฉํ ๋ฆฌ ๋ฉ์๋๋ฅผ ๋ค๋ฅธ ์๋ก์ด ํด๋์ค์๊ฒ ๊ตฌํํ๋๋ก ํ๋ค.Dao
์ Static Type์ Dynamic Type์ ๊ด๊ณ์ํค๋ ์ฑ
์์ ํด๋ผ์ด์ธํธ์๊ฒ ์ ๊ฐํ๋ค.OCP(Open-Closed Principle)
ํด๋์ค๋ ๋ชจ๋์ ํ์ฅ์๋ ์ด๋ ค ์์ด์ผ ํ๊ณ , ๋ณ๊ฒฝ์๋ ๋ซํ ์์ด์ผ ํ๋ค.
๊ทธ ์๋ก, ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ๋ฉด ํ์ฅ์ ์ํด์๋ ๊ฐ๋ฐฉ๋์ด ์์ง๋ง, ์ธํฐํ์ด์ค๋ฅผ ์ด์ฉํ๋ ํด๋์ค๋ ํ์ฅ์ผ๋ก ์ธํ ๋ณํ๋ ๋ถํ์ํ๊ฒ ์ผ์ด๋์ง ์๋๋ก ๋ซํ์๋ค.
๋์ ์์ง๋
๋ณํ๊ฐ ์ผ์ด๋ ๋ ํด๋น ๋ชจ๋์์ ๋ณํ๋ ๋ถ๋ถ์ด ํฌ๋ค๋ ๊ฒ. ์ด๋ ๊ฒ ๋๋ฉด ๋ฐ๋์ง ์๋ ๋ถ๋ถ๋ค์ ๋ํด ์ ๊ฒฝ ์ธ ํ์๊ฐ ์๋ค.
์ฆ, ํ๋์ ๋ชจ๋์ด ํ๋์ ๊ด์ฌ์ฌ์๋ง ์ง์ค๋์ด ์๋ค๋ ๊ฒ
๋ฎ์ ๊ฒฐํฉ๋
๊ด์ฌ์ฌ๊ฐ ๋ค๋ฅธ ๋ชจ๋๋ผ๋ฆฌ๋ ๋์จํ ์ฐ๊ฒฐ์ ์ ์งํ๋ ๊ฒ. ๋ชจ๋๋ผ๋ฆฌ์ ๋ ๋ฆฝ์ฑ์ ๋์ธ๋ค๋ ๊ฒ์ด๊ณ , ํ๋์ ๋ชจ๋์ ๋ํด ์ ๊ฒฝ ์ธ ๋๋ ๋ค๋ฅธ ๋ชจ๋์ ๋ํด ์ต์ํ์ ์ ๋ณด๋ง ์์๋ ๋๋ค๋ ๊ฒ์ด๋ค.
์ด๋ฅผ ํตํด ๋ณํ์ ๋์ํ๊ธฐ ์ฌ์์ง๊ณ , ๊ตฌ์ฑ์ด ๊น๋ํด์ง๋ค.
์ฆ, ๊ฒฐํฉ๋๋ ํ๋์ ๋ชจ๋์ ๋ณํ์ ๋ํด ๊ด๊ณ๋ฅผ ๋งบ๋ ๋ค๋ฅธ ๋ชจ๋์๊ฒ ๋ณํ๋ฅผ ์๊ตฌํ๋ ์ ๋
์์ ์ ๊ธฐ๋ฅ(๋งฅ๋ฝ = Context)์์ ํ์์ ๋ฐ๋ผ ๋ณ๊ฒฝ์ด ํ์ํ ์๊ณ ๋ฆฌ์ฆ์ ์ธํฐํ์ด์คํ ํ์ฌ ํต์งธ๋ก ์ธ๋ถ๋ก ๋ถ๋ฆฌ์ํค๊ณ , ์ด๋ฅผ ๊ตฌํํ ๊ตฌ์ฒด์ ์ธ ์๊ณ ๋ฆฌ์ฆ ํด๋์ค๋ฅผ ํ์์ ๋ฐ๋ผ ๋ฐ๊ฟ์ ์ฌ์ฉํ ์ ์๊ฒ ํ๋ ํจํด.
์๊ณ ๋ฆฌ์ฆ
๋ ๋ฆฝ์ ์ธ ์ฑ ์์ผ๋ก ๋ถ๋ฆฌ๊ฐ ๊ฐ๋ฅํ ๊ธฐ๋ฅ
๊ทธ ์๋ก, Dao
์์ getConnection()
์๊ณ ๋ฆฌ์ฆ์ ํต์งธ๋ก ๋ถ๋ฆฌ์์ผ ConnectionMaker
๋ผ๋ ์ธํฐํ์ด์ค๋ก ์ ์ํ๊ณ , ์ด๋ฅผ ๊ตฌํํ ํด๋์ค์ ๋ฐ๋ผ ์ ๋ต์ ๋ฐ๊ฟ๊ฐ๋ฉด์ ์ฌ์ฉํ ์ ์๊ฒ ๋ถ๋ฆฌํ์๋ค.
Ioc(Inversion of Control)
์ง๊ธ๊น์ง ๋ฆฌํฉํ ๋ฆฌํ Dao
๋ ๊ด์ฐฎ์ง๋ง, ์์ง ๊ฐ์ ์ฌํญ์ด ๋จ์๋ค. Dao
๋ฅผ ์ฌ์ฉํ๋ ํด๋ผ์ด์ธํธ์์๋ ๋ณธ๋ ๊ด์ฌ์ฌ๊ฐ ์๋ ์ผ์ ๋ ๊ฐ์ง๋ ํ๊ณ ์๋ค.
ConnectionMaker
๊ฐ์ฒด์ ์์ฑDao
์ ๊ฐ์ฒด๋ฅผ ์ฐ๊ฒฐ (๊ด๊ณ ์ฃผ์
)๋ฐ๋ผ์ ์ด ๋ ๊ฐ์ง ๊ธฐ๋ฅ ์ญ์ ๋ฐ๋ก ๋ถ๋ฆฌ๋์ด์ผ ํ๋ค.
์์ ๋ ๊ธฐ๋ฅ์ DaoFactory
๋ผ๋ ๊ฐ์ฒด๋ฅผ ์๋ก ๋ง๋ค์ด์ ๋งก๊ธฐ๋๋ก ํ์. userDao()
๋ผ๋ ํจ์์์ ์ด๋ฐ ๊ธฐ๋ฅ์ ๋งก๊ฒ ๋ ๊ฒ์ด๋ฉฐ, ๊ฒฐ๊ณผ์ ์ผ๋ก ์์ฑ๋ Dao
๋ฅผ ๋ฐํํ๊ฒ ๋ ๊ฒ์ด๋ค.
ํฉํ ๋ฆฌ
๊ฐ์ฒด๋ฅผ ์์ฑํ์ฌ ๋ฐํํ๋ ํด๋์ค. ์ค๋ธ์ ํธ๋ฅผ ์์ฑํ๋ ์ชฝ๊ณผ ์์ฑ๋ ์ค๋ธ์ ํธ๋ฅผ ์ฌ์ฉํ๋ ์ชฝ์ ์ญํ ๊ณผ ์ฑ ์์ ๋ถ๋ฆฌํ๊ธฐ ์ํด์ ๋์ ๋ ๊ฐ๋ .
ํด๋ผ์ด์ธํธ๋ ํฉํ ๋ฆฌ ๊ฐ์ฒด๋ฅผ ํตํด Dao
๋ฅผ ์์ฑํ ๊ฒ์ด๋ฉฐ, ๋ ๊ฐ์ง ๊ธฐ๋ฅ์ด ๋ถ๋ฆฌ๋์๋ค.
์ด๋ก์จ Dao
์ ConnectionMaker
์ ๊ฐ๊ฐ ํต์ฌ์ ์ธ ๋ฐ์ดํฐ๋ก์ง, ๊ธฐ์ ๋ก์ง์ ๋ด๋นํ๊ณ ์๊ณ , DaoFactory
๋ ๊ฐ์ฒด๋ค์ ๊ตฌ์ฑํ๊ณ ๊ด๊ณ๋ฅผ ์ ์ํ๋ ์ฑ
์์ ๋ด๋นํ๊ฒ ๋์๋ค.
์ฆ, ์ ์๋ ์ค์ง์ ์ธ ๋ก์ง์ ๋ด๋นํ๋ ์ปดํฌ๋ํธ, ํ์๋ ์ปดํฌ๋ํธ์ ๊ตฌ์กฐ์ ๊ด๊ณ๋ฅผ ์ ์ํ ์ค๊ณ๋ ๊ฐ์ ์ญํ ์ด๋ผ๊ณ ๋ณผ ์ ์๋ค.
DaoFactory
์์ ์ฌ๋ฌ Dao
๋ฅผ ์์ฑํ๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
๊ฐ ํฉํ ๋ฆฌ ๋ฉ์๋๋ง๋ค ConnectionMaker
์ ์์ฑํ๋ ์ฝ๋๊ฐ ์ค๋ณต๋์ ๋ํ๋๋ค. ์ฝ๋ ์ค๋ณต์ด ๋ฐ์ํ๋ฉด ์ด ์ฝ๋๋ฅผ ์์ ํ ๋ ์๋ฏธ์์ด ๋ฐ๋ณต์ ์ผ๋ก ๊ฐ์ ์ฝ๋๋ฅผ ์์ ํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์ด ๋ถ๋ถ ์ญ์ DaoFactory
์์ ์๋ก์ด ๋ฉ์๋๋ก ๋นผ๋ด๋ ๊ฒ์ด ์ข๋ค.
์ผ๋ฐ์ ์ธ ํ๋ก๊ทธ๋จ์์๋?
main()
์์ ์์ํด ์ฌ๊ธฐ์์ ์ฌ์ฉํ ๊ฐ์ฒด๋ฅผ ๊ฒฐ์ ํ์ฌ ์์ฑํ๊ณ , ํด๋น ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ฌ๊ท์ ์ผ๋ก ๋ฐ๋ณต๋๋ค.
์ ์ด์ ์ญ์ ์ด๋?
์ด๊ฒ์ด ๊ฐ๋ฅํ ์ด์ ๋, ๋ชจ๋ ์ ์ด์ ๊ถํ์ ์์ ์ด ์๋ ๋ค๋ฅธ ๋์์๊ฒ ์์ํ๊ธฐ ๋๋ฌธ์ด๋ค.
์์
์๋ธ๋ฆฟ์ ๊ฒฝ์ฐ ์๋ธ๋ฆฟ์ ์ฝ๋๋ฅผ ์ง์ ์คํํ ์ ์๋ ์๋จ์ ์์ง๋ง, ๊ทธ ์ ์ด ๊ถํ์ ๊ฐ์ง ์ปจํ ์ด๋๊ฐ ์๋ธ๋ฆฟ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ณ ์ฌ์ฉํ๊ฒ ๋๋ค.
getConnection()
์ ๊ฒฝ์ฐ๋ ์ ์ด๊ถ์ ์์ ํ
ํ๋ฆฟ ๋ฉ์๋์ ๋๊ธฐ๊ณ ๊ทธ ์์ ์ ํ์ํ ๋ ํธ์ถ๋์ด ์ฌ์ฉ๋๋ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋ค.
ํ๋ ์์ํฌ๋ ์์ฑํ ์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋๊ฐ ์์ ๊ฐ๋ ์ธ ํ๋ ์์ํฌ์ ์ํด ์ฌ์ฉ๋๋ ๊ตฌ์กฐ์ด๋ค.
๋ผ์ด๋ธ๋ฌ๋ฆฌ vs ํ๋ ์์ํฌ
๋ผ์ด๋ธ๋ฌ๋ฆฌ : ์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋๊ฐ ์ง์ ํ๋ฆ์ ์ ์ดํ๊ณ , ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋จ์ํ ์ฌ์ฉ๋ ๋ฟ์ด๋ค.
ํ๋ ์์ํฌ : ์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋๋ ํ๋ ์์ํฌ์ ์ํด ์ฌ์ฉ๋๊ธฐ๋ง ํ ๋ฟ, ํ๋ฆ์ ์ ์ด๋ ํ๋ ์์ํฌ๊ฐ ํ๋ค.
์ด๋ฐ ๊ด์ ์์, DaoFactory
์ญ์ ํ๋์ IoC ํ๋ ์์ํฌ๋ผ๊ณ ๋ณผ ์๋ ์๋ค.
๋น (Bean)
Spring Container๊ฐ ์ ์ด๊ถ์ ๊ฐ์ง๊ณ ์ง์ ์์ฑํ๊ณ ๊ด๊ณ๋ฅผ ๋ถ์ฌํ๋ ๋์์ด ๋๋ ๊ฐ์ฒด
SpringBean์ JavaBean๊ณผ ๋น์ทํ๊ฒ ๊ฐ์ฒด ๋จ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ปดํฌ๋ํธ์ด๋ค. ๋์์ Spring Container๊ฐ ์์ฑ/๊ด๊ณ์ค์ /์ฌ์ฉ ๋ฑ์ ์ ์ดํด์ฃผ๋ IoC ๊ฐ๋ ์ด ์ ์ฉ๋ ๊ฐ์ฒด์ด๊ธฐ๋ ํ๋ค.
๋น ํฉํ ๋ฆฌ (Bean Factory)
Bean์ ์์ฑ/๊ด๊ณ์ค์ ๋ฑ์ ์ ์ด๋ฅผ ๋ด๋นํ๋ IoC ๊ฐ์ฒด
๋น์ ๋ฑ๋ก/์์ฑ/์กฐํ/๋ฐํ/๊ด๋ฆฌ ํ๋ ๊ธฐ๋ฅ์ ๋ด๋นํ๋ฉฐ, ๋ณดํต์ ๋น ํฉํ ๋ฆฌ๋ฅผ ํ์ฅํ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๋ฅผ ์ฌ์ฉํ๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ (Application Context)
Bean Factory๋ฅผ ์์ํ์ฌ ํ์ฅํ ๊ฒ์ผ๋ก IoC ๋ฐฉ์์ ๋ฐ๋ผ ๋ง๋ค์ด์ง ์ผ์ข ์ Bean Factory. Bean Factory์ ๋์ผํจ.
๋จ, ์ฝ๋ ๋ด์ ์ ์ด์ ๊ดํ ์ ๋ณด๊ฐ ๋ค์ด์๋ ๊ฒ์ด ์๋๋ผ ๋ณ๋์ ์ค์ ์ ๋ณด๋ฅผ ๋ด์ ๋ฌด์ธ๊ฐ๋ฅผ ๊ฐ์ ธ์ ํ์ฉํ๋ IoC ์์ง.
์ฆ, ์คํ๋ง์ด ์ ๊ณตํ๋ ๊ฐ์ข ๋ถ๊ฐ ์๋น์ค๋ฅผ ์ถ๊ฐ๋ก ์ ๊ณตํ๋ ๊ฒ์ด๋ค.
๋น ํฉํ ๋ฆฌ vs ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ
๊ธฐ๋ณธ์ ์ผ๋ก ๋์ ๋์ผํ ๊ฐ๋ ์ ๊ฐ๋ฆฌํค๋ ๋ง์ด์ง๋ง, ์ค์ ์ ๋๋ ๋ถ๋ถ์ด ๋ค๋ฅด๋ค.
๋น ํฉํ ๋ฆฌ : ๋น์ ์ ์ดํ๋ IoC ๊ธฐ๋ณธ ๊ธฐ๋ฅ์ ์ด์ ์ด ๋ง์ถฐ์ง ๊ฒ.
์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ : ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ ๊ฑธ์ณ ๋ชจ๋ ๊ตฌ์ฑ์์์ ์ ์ด๋ฅผ ๋ด๋นํ๋ IoC ์์ง์ด๋ผ๋ ์๋ฏธ๊ฐ ๋ถ๊ฐ๋ ๊ฒ.
์ค์ ์ ๋ณด (Configuration, Metadata)
์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๊ฐ IoC๋ฅผ ์ ์ฉํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ๋ฉํ์ ๋ณด๋ก, ์ปจํ ์ด๋์ ์ด๋ค ๊ธฐ๋ฅ์ ์ธํ ํ๊ฑฐ๋ ์กฐ์ ํ๋ ๊ฒฝ์ฐ์๋ ์ฌ์ฉํ์ง๋ง ๋ณดํต IoC ์ปจํ ์ด๋์ ์ํด ๊ด๋ฆฌ๋๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ๊ตฌ์ฑํ ๋ ์ฌ์ฉ๋๋ค.
์ปจํ ์ด๋ / IoC ์ปจํ ์ด๋
์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๋ ๋น ํฉํ ๋ฆฌ๋ฅผ ์ด๋ฅด๋ ๋ง์ด๋ค.
IoC ์ปจํ ์ด๋์ ๊ฒฝ์ฐ ์ฃผ๋ก ๋น ํฉํ ๋ฆฌ ๊ด์ ์์ ์ด์ผ๊ธฐํ ๋ ์ฐ์ด๋ฉฐ, ๊ทธ๋ฅ ์ปจํ ์ด๋/์คํ๋ง ์ปจํ ์ด๋๋ผ๊ณ ํ ๋๋ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๋ฅผ ์ถ์์ ์ผ๋ก ๊ฐ๋ฆฌํฌ ๋ ์ฌ์ฉํ๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๊ฐ ์ด๋ฆ์ด ๊ธธ๊ธฐ ๋๋ฌธ์ ์คํ๋ง ์ปจํ ์ด๋๋ผ๊ณ ๋ถ๋ฅด๊ธฐ๋ ํ๋ฉฐ, ์คํ๋ง์ด๋ผ๊ณ ๋ถ๋ฅด๊ธฐ๋ ํ๋ค. ์ด ๋๋ โ์คํ๋ง์ ๋น์ ๋ฑ๋กํ๊ณ โ ๋ผ๋ ์์ผ๋ก ๋งํ๊ธฐ๋ ํ๋ค.
์คํ๋ง ํ๋ ์์ํฌ
IoC ์ปจํ ์ด๋, ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๋ฅผ ํฌํจํด์ ์คํ๋ง์ด ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ ํตํ์ด ๋งํ ๋ ์ฌ์ฉํ๋ค.
์ค์ ์ ๋ณด ์์ฑ
@Configuration
: ์ด ํด๋์ค๋ ์ ํ๋ฆฌ์ผ์ด์
์ปจํ
์คํธ๊ฐ ์ฌ์ฉํ ์ค์ ์ ๋ณด๋ผ๋ ๊ฒ์ ์๋ฆผ@Bean
: ์ค๋ธ์ ํธ ์์ฑ์ ๋ด๋นํ๋ IoC์ฉ ๋ฉ์๋๋ผ๋ ํ์@Configuration
public class DaoFactory {
@Bean
public UserDao userDao() {
UserDao dao = new UserDao(connectionMaker());
return dao;
}
@Bean
public ConnectionMaker connectionMaker() {
ConnectionMaker connectionMaker = new DConnectionMaker();
return connectionMaker;
}
}
์ด๋ ธํ ์ด์ ์ ์๋ฐ ์ฝ๋์ธ ๊ฒ ๊ฐ์ง๋ง, ์ฌ์ค์ XML ๊ฐ์ ์คํ๋ง ์ ์ฉ ์ค์ ์ ๋ณด์ด๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ ์์ฑ
public class UserDaoTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(DaoFactory.class);
UserDao dao = context.getBean("userDao", UserDao.class);
// ....
}
}
AnnotationConfigApplicationContext
๋ApplicationContext
๋ฅผ ๊ตฌํํ ํด๋์ค ์ค ํ๋๋ก,@Configuration
์ด ๋ถ์ ์๋ฐ ์ฝ๋๋ฅผ ์ค์ ์ ๋ณด๋ก ์ฌ์ฉํ๊ธฐ ์ํด ์ฐ์ธ๋ค.
getBean()
์ApplicationContext
๊ฐ ๊ด๋ฆฌํ๋ ์ค๋ธ์ ํธ๋ฅผ ์์ฒญํ๋ ๋ฉ์๋์ด๋ค.
getBean()
์ ๋ฐํ ํ์ ์Object
์ด๋ฏ๋ก ํ๋ณํ์ด ํ์ํ์ง๋ง, Java5 ์ด์๋ถํฐ๋ ์ ๋ค๋ฆญ ๋ฉ์๋ ๋ฐฉ์์ ํตํด ๋ฆฌํด ํ์ ์ ์ ๊ณต ๋ฐ๋ ์์ผ๋ก ๋ ๊น๋ํ๊ฒ ๊ตฌํํ ์ ์๋ค.
์ ์ค๋ธ์ ํธ ํฉํ ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ง ์๋๊ฐ?
์ง์ ๊ตฌํํ ์ค๋ธ์ ํธ ํฉํ ๋ฆฌ๋ ์ ํ์ ์ผ๋ก ๊ฐ์ฒด ์์ฑ/๊ด๊ณ์ค์ ์ ํ์ง๋ง, ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ IoC๋ฅผ ์ ์ฉํด ๊ด๋ฆฌํ ๋ชจ๋ ๊ฐ์ฒด์ ๋ํ ์์ฑ/๊ด๊ณ ์ค์ ์ ํ๋ค๋ ์ฐจ์ด๊ฐ ์๋ค.
๋ํ ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ๋ ๊ฐ์ฒด๋ฅผ ์ง์ ์์ฑํ๊ณ ํ ๋นํ๋ ์ฝ๋๊ฐ ์๋ ๋์ ์ ๊ทธ๊ฒ์ ๋ํ ์ ๋ณด๋ฅผ ๋ณ๋์ ์ค์ ์ ๋ณด๋ฅผ ํตํด ์ป๋๋ค. (๋๋ก๋ ์ธ๋ถ์ ์ค๋ธ์ ํธ ํฉํ ๋ฆฌ์ ๊ทธ ์์ ์ ์์ํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์ฌ์ฉํ๊ธฐ๋ ํ๋ค.)
์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์คํธ = ์ค๋ธ์ ํธ ํฉํ ๋ฆฌ = IoC ์ปจํ ์ด๋ = ์คํ๋ง ์ปจํ ์ด๋ = ๋น ํฉํ ๋ฆฌ
์ ํํ๋
ApplicationContext
๊ฐBeanFactory
์ธํฐํ์ด์ค๋ฅผ ์์ํ๋ค.
๋์ ๋ฐฉ์
@Configuration
๋ฑ์ด ๋ถ์ ํด๋์ค๋ฅผ ์ค์ ์ ๋ณด๋ก ๋ฑ๋กํด๋๊ณ @Bean
์ด ๋ถ์ ๋ฉ์๋๋ค์ ์ด๋ฆ์ ๊ฐ์ ธ์ ์ด๋ฆ๊ณผ ๋ฉ์๋๋ฅผ ๋งคํํด๋ Bean List๋ฅผ ์์ฑํด๋๋ค.getBean()
์ด ํธ์ถ๋๋ฉด Bean List์์ ํด๋น๋๋ ์ด๋ฆ์ ๋ฉ์๋๋ฅผ ์ฐพ์ ํธ์ถํ๊ณ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํํ๋ค.์ฅ์
ํด๋ผ์ด์ธํธ๊ฐ ํ์ํ ๋๋ง๋ค ํฉํ ๋ฆฌ ํด๋์ค๋ฅผ ๊ตฌํํ ํ์๊ฐ ์๋ค.
์ง์ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋์ , ์ด๋ ธํ ์ด์ , XML ๋ฑ์ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ผ๋ก IoC ์ค์ ์ ํ ์ ์๋ค.
์ข ํฉ IoC ์๋น์ค๋ฅผ ์ ๊ณตํ๋ค.
์์ฑ/๊ด๊ณ ์ค์ ๋ฟ ์๋๋ผ ์์ฑ ๋ฐฉ์/์์ /์ ๋ต ๋ฑ์ ์ค์ ํ ์ ์๊ณ ์๋์์ฑ/ํ์ฒ๋ฆฌ ๋ฑ ๋ค์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค. ๋ํ ๋น์ด ์ฌ์ฉํ ์ ์๋ ๊ธฐ๋ฐ ๊ธฐ์ ์๋น์ค๋ ์ธ๋ถ ์์คํ ๊ณผ์ ์ฐ๋ ๋ฑ์ ์ปจํ ์ด๋ ์ฐจ์์์ ์ง์ํด์ฃผ๊ธฐ๋ ํ๋ค.
๋น์ ๊ฒ์ํ๋ ๋ค์ํ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ค.
๋ฉ์๋ ์ด๋ฆ ๋ฟ๋ง์ด ์๋๋ผ ํ์ , ํน์ ์ด๋ ธํ ์ด์ ๋ฑ์ผ๋ก ์ค์ ๋ ๋น๋ ์ฐพ์ ์ ์๋ค.
์ง์ ๊ตฌํํ DaoFactory
์์ ๊ฐ์ฒด๋ฅผ ์ฌ๋ฌ๋ฒ ์์ฑํ๋ ๊ฒ๊ณผ ApplicationContext
๋ฅผ ํตํด์ ๊ฐ์ฒด๋ฅผ ์ฌ๋ฌ๋ฒ ์์ฑํ๋ ๊ฒ์๋ ๋ฌด์จ ์ฐจ์ด๊ฐ ์์๊น?
๋น์ฐํ๊ฒ๋ DaoFactory
๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ๊ทธ ์์ชฝ์ ๊ตฌํ์ผ๋ก ์ธํด ์๋ก์ด ๊ฐ์ฒด๊ฐ ๊ณ์ ์์ฑ๋์ด ๋์จ๋ค. ํ์ง๋ง, ApplicationContext
๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ๋๋๊ฒ๋ ๊ฐ์ ๊ฐ์ฒด๊ฐ ๋ฐํ๋๋ค!
๋์ผ์ฑ (Identity) : ๊ฐ์ฒด์ ์ฃผ์๊ฐ ๊ฐ์์ง, ์ฆ ๋ฌผ๋ฆฌ์ ์ผ๋ก ๊ฐ์ ๊ฐ์ฒด์ธ์ง?
๋๋ฑ์ฑ (Equality) : ๊ฐ์ฒด์ ์ฃผ์๋ ๋ค๋ฅด์ง๋ง ๋๋ฑ์ ํ๋จ ๊ธฐ์ค์ ๋ฐ๋ผ ๊ฐ์ด ๊ฐ์์ง?
์์ ๊ฐ์ ํ์์ด ๋ฐ์ํ๋ ์ด์ ๋, ApplicationContext
๊ฐ ์ฑ๊ธํด์ ์ ์ฅํ๊ณ ๊ด๋ฆฌํ๋ ์ฑ๊ธํด ๋ ์ง์คํธ๋ฆฌ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
๋ณ๋ค๋ฅธ ์ค์ ์ ํ์ง ์์ผ๋ฉด, ๋ด๋ถ์์ ์์ฑํ๋ ๋น ๊ฐ์ฒด๋ฅผ ๋ชจ๋ ์ฑ๊ธํด์ผ๋ก ๋ง๋ ๋ค.
๋์์ธ ํจํด์์์ ์ฑ๊ธํด๊ณผ ๊ฐ๋ ์ ๋น์ทํ์ง๋ง, ๊ตฌํ ๋ฐฉ์์ ์์ ํ ๋ค๋ฅด๋ค.
์ด์ ๊ฐ์ด ์ฒ๋ฆฌ๋๋ ์ด์ ๋ ์คํ๋ง์ด ์๋ฒ ํ๊ฒฝ์ ๊ณ ๋ คํ์ฌ ์ค๊ณ๋์๊ธฐ ๋๋ฌธ์ด๋ค. ์๋ฒ์์๋ TPS๋ผ๋ ์ฉ์ด๊ฐ ์์ ์ ๋๋ก ์ด๋น ์๋ง์ ์์ฒญ์ด ๋ค์ด์ค๋๋ฐ, ์ด ์์ฒญ์ ์ฒ๋ฆฌํ ๋๋ง๋ค ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๊ฒ์ ๊ต์ฅํ ๋ญ๋น์ผ ์ ์๋ค. ๊ทธ๋์ ์ํฐํ๋ผ์ด์ฆ ๋ถ์ผ์์๋ ์๋น์ค ์ค๋ธ์ ํธ๋ผ๋ ๊ฐ๋ ์ ์ผ์ฐ๋ถํฐ ์ฌ์ฉํด์๊ณ , ์๋ฐ์์๋ ์๋ธ๋ฆฟ์ด ๊ธฐ๋ณธ์ด๊ณ , ์ด ์ญ์ ์ฑ๊ธํด์ผ๋ก ์กด์ฌํ๋ค.
์ฑ๊ธํด ํจํด (Singleton Pattern)
ํ๋ก๊ทธ๋จ์์ ๊ฐ์ฒด๊ฐ ๋จ ํ๋๋ง ์กด์ฌํ๋๋ก ๊ฐ์ ํ๋ ํจํด. ์ฃผ๋ก ์ฌ๋ฌ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๊ณต์ ํ๋ ๊ฒฝ์ฐ์ ์ฌ์ฉ๋๋ค.
๋จ, ์ฑ๊ธํด ํจํด์ ์ฌ๋ฌ ๋ฌธ์ ์ ์ ๋ฐ์์ํฌ ์ ์๊ธฐ ๋๋ฌธ์ ๊ต์ฅํ ์กฐ์ฌํด์ ์ฌ์ฉํด์ผ ํ๋ค.
์ผ๋ฐ์ ์ธ ์ฑ๊ธํด ํจํด์ Dao
๊ฐ์ฒด์ ์ ์ฉํ์ ๋ ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ์ ์ด ๋ฐ์ํ๋ค.
DaoFactory
์์ Dao
๋ฅผ ์์ฑํ์ฌ ๋ฐํํ ์ ์๋ค.์๋ฒ ํ๊ฒฝ์์ ์ฑ๊ธํด์ ์ฐ๊ณ ๋ ์ถ์๋ฐ ์ฑ๊ธํด ํจํด๊ณผ private ์์ฑ์๋ฅผ ์ด์ฉํ ๋น์ ์์ ์ธ ๋ฐฉ๋ฒ์ ์ฐ๊ธฐ ํ๋ค๊ธฐ ๋๋ฌธ์ ์คํ๋ง์์๋ ์ฑ๊ธํด ๋ ์ง์คํธ๋ฆฌ๋ฅผ ์ง์ํ๋ค.
์ฑ๊ธํด ๋ ์ง์คํธ๋ฆฌ (Singleton Registry)
์คํ๋ง์ด ์ง์ ์ฑ๊ธํด ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ณ ๊ด๋ฆฌํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ๊ฒ.
์ด๊ฒ์ ํ๋ฒํ public ์์ฑ์๋ฅผ ๊ฐ์ง ์๋ฐ ํด๋์ค๋ฅผ ์ฑ๊ธํด์ผ๋ก ํ์ฉํ ์ ์๊ฒ ๋ง๋ค์ด์ค๋ค. ๊ทธ๋ ๊ฒ ํ ์ ์๋ ์ด์ ๊ฐ ํด๋์ค์ ์ ์ด๊ถ์ IoC ๋ฐฉ์์ ์ปจํ ์ด๋์๊ฒ ๋๊ธฐ๋ฉด ํด๋น ์ปจํ ์ด๋๊ฐ ๊ฐ์ฒด ์์ฑ์ ๋ํ ๋ชจ๋ ๊ถํ์ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ฐ์ฒด๊ฐ ์ฑ๊ธํด์ผ๋ก ์กด์ฌํ ์ ์๊ฒ ๊ด๋ฆฌ๋ฅผ ํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ฆ, IoC ์ปจํ ์ด๋์ ์ ์ด ์ญํ ์ ํตํด์ ๋น์ ์ฑ๊ธํค์ผ๋ก ๋ง๋๋ ๊ฒ์ด๋ค.
์๋ฒ ํ๊ฒฝ์์๋ ์ฌ๋ฌ ์์ฒญ์ด ๋ฉํฐ์ค๋ ๋ฉ์ผ๋ก ์ฒ๋ฆฌ๋๋ฏ๋ก ์ฑ๊ธํด ๊ฐ์ฒด์ ์ํ๋ฅผ ๋ค๋ฃฐ ๋ ์ฃผ์ํด์ผํ๋ค. ์๋น์ค ํํ์ ์ค๋ธ์ ํธ๋ก ์ฌ์ฉ๋๋ ๊ฒฝ์ฐ ๋ด๋ถ์ ๋ณํ๋ ์ํ๋ฅผ ๊ฐ์ง ์๋ Stateless๋ก ๋ง๋ค์ด์ ธ์ผ ํ๋ค. ์ด ๋ ์์ฒญ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ ๊ฐ์ ๋ํ ์ ๋ณด๋ ํ๋ผ๋ฏธํฐ, ์ง์ญ ๋ณ์, ๋ฐํ ๊ฐ ๋ฑ์ ์ด์ฉํ๋ฉด ๋๋ค.
Default Scope : ์ฑ๊ธํด
๊ฐ์ ๋ก ์ ๊ฑฐํ์ง ์์ผ๋ฉด ์คํ๋ง ์ปจํ ์ด๋๊ฐ ์กด์ฌํ๋ ๋์ ๊ณ์ ์ ์ง๋จ
Prototype Scope : ๋น์ ์์ฒญํ ๋๋ง๋ค ์๋ก ์์ฑ
Request Scope : HTTP ์์ฒญ์ด ์๊ธธ ๋๋ง๋ค ์์ฑ
Session Scope : ์น์ ์ธ์ ๊ณผ ์ค์ฝํ๊ฐ ์ ์ฌ
โฆ..
IoC๋ผ๋ ์ฉ์ด๋ ๋งค์ฐ ๋์จํ๊ฒ ์ ์๋์ด ํญ ๋๊ฒ ์ฌ์ฉ๋๋ค. ๋ฐ๋ผ์ ์คํ๋ง์ด ์ ๊ณตํ๋ IoC ๋ฐฉ์์ ์ค์ ์ ์ง์ด์ฃผ๊ธฐ ์ํด์ ์์กด๊ด๊ณ ์ฃผ์ ์ด๋ผ๋ ์ฉ์ด๊ฐ ๋ฑ์ฅํ๋ค.
์์กด๊ด๊ณ ์ฃผ์ (Dependency Injection = DI)
์ค๋ธ์ ํธ ๋ ํผ๋ฐ์ค๋ฅผ ์ธ๋ถ๋ก๋ถํฐ ์ฃผ์ ๋ฐ๊ณ , ์ด๋ฅผ ํตํด ์ฌํ ์ค๋ธ์ ํธ์ ๋์ ์ผ๋ก ์์กด๊ด๊ณ๊ฐ ๋ง๋ค์ด์ง๋ ๊ฒ.
์์กด๊ด๊ณ๋ ๋ฐฉํฅ์ฑ์ด ์กด์ฌํ๊ณ , $A\rightarrow B$๋ผ๋ ์์กด๊ด๊ณ๊ฐ ์๋ค๋ฉด $B$๊ฐ ๋ณํ๋ฉด $A$๋ ์ํฅ์ ๋ฐ๋๋ค๋ ๊ฒ์ ๋ปํ๋ค.
Dao
์ ConnectionMaker
๋ํ ์ด๋ฌํ ๊ด๊ณ์ด๋ค.
์ด ๋ ConnectionMaker
๊ฐ ์ธํฐํ์ด์ค๊ฐ ์๋ ์ผ๋ฐ ํด๋์ค๋ผ๋ฉด ๊ฐํ๊ฒ ์์กดํ๊ณ ์๋ค๋ ๋ป์ด ๋๊ณ , ์ฝ๋๊ฐ ์กฐ๊ธ์ด๋ผ๋ ๋ณํ๋ค๋ฉด Dao
์์๋ ์ ๊ฒฝ์ ์จ์ผํ ๊ฒ์ด๋ค.
๊ทธ๋ฌ๋ ์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ์ธํฐํ์ด์ค์ ๋ํด์๋ง ์์กด๊ด๊ณ๋ฅผ ๋ง๋ค์ด๋๋ฉด ์ธํฐํ์ด์ค ๊ตฌํ ํด๋์ค์๋ ๊ด๊ณ๊ฐ ๋์จํด์ง๋ฉฐ ๋ณํ๋ฅผ ๋ ๋ฐ๋ ์ํ๊ฐ ๋๋ค.
ํ์ง๋ง ์ด์ ๊ฐ์ด UML์์ ๋งํ๋ ์์กด๊ด๊ณ๋ ์ค๊ณ ๋ชจ๋ธ์ ๊ด์ ์์ ๋ณด๋ ๊ฒ์ด๊ณ , ์ด๋ฅผ ํตํด ๋๋ฌ๋์ง ์๋ ์์กด๊ด๊ณ๊ฐ ์กด์ฌํ๋ค.
๋ฐํ์ ์์ ๋ง๋ค์ด์ง๋ ์์กด๊ด๊ณ์ด๋ค.
์๋ฅผ ๋ค์ด Dao
์์๋ ์ฝ๋ ์์ฑ ์์ ์ ์ด๋ค ConnectionMaker
์ ๊ด๊ณ๋ฅผ ๋งบ์ ์ง ์ ์ ์๋ค. ๊ทธ๊ฒ์ ํด๋ผ์ด์ธํธ ์ธก์ ์ ํ์ ๋ฐ๋ผ์ ๋ฌ๋ผ์ง๊ธฐ ๋๋ฌธ์ด๋ค.
์์กด๊ด๊ณ ์ฃผ์ ์ด๋ ์ด๋ ๊ฒ ๊ตฌ์ฒด์ ์ธ ์์กด ์ค๋ธ์ ํธ์ ๊ทธ๊ฒ์ ์ฌ์ฉํ ์ฃผ์ฒด(์ผ๋ฐ์ ์ผ๋ก ํด๋ผ์ด์ธํธ) ์ค๋ธ์ ํธ๋ฅผ ๋ฐํ์ ์์ ์ฐ๊ฒฐํด์ฃผ๋ ์์ ์ ๋งํ๋ค.
์์กด ์ค๋ธ์ ํธ (Dependent Object)
๋ฐํ์ ์์ ์์กด๊ด๊ณ๋ฅผ ๋งบ๋ ๋์
์ฆ, ์์กด๊ด๊ณ ์ฃผ์ ์ ๋ค์ ์ธ ๊ฐ์ง ์กฐ๊ฑด์ ์ถฉ์กฑํ๋ ์์ ์ด๋ค.
2๋ฒ์ ์ข์ ์์๋ก Dao
์์ ์ง์ MyConnectionMaker
๋ฅผ ์์ฑํ๋ ๊ฒ์ ์ 3์์ธ ํด๋ผ์ด์ธํธ๋ ํฉํ ๋ฆฌ, ์ ํ๋ฆฌ์ผ์ด์
์ปจํ
์คํธ์๊ฒ ๋งก๊ธด ๊ฒ์ ์๊ฐํ๋ฉด ๋๋ค. ์ด๋ฌํ ์ 3์๋ค์ DI ์ปจํ
์ด๋๋ผ๊ณ ๋ถ๋ฅผ ์ ์๋ค.
DI ์ปจํ ์ด๋
๋ ๊ฐ์ฒด ์ฌ์ด์ ๋ฐํ์ ์์กด๊ด๊ณ๋ฅผ ์ค์ ํด์ฃผ๋ ์์กด ๊ด๊ณ ์ฃผ์ ์์ ์ ์ฃผ๋ํ๋ ์กด์ฌ.
์ค๋ธ์ ํธ ์์ฑ/์ด๊ธฐํ/์ ๊ณต ๋ฑ์ ์์ ์ ์ํํจ.
์ด์ฒ๋ผ DI๋ ์์ ์ด ์ฌ์ฉํ ๊ฐ์ฒด์ ๋ํ ์ ํ, ์์ฑ ์ ์ด๊ถ์ ์ธ๋ถ๋ก ๋๊ธฐ๊ณ , ์๋์ ์ผ๋ก ์ฃผ์ ๋ฐ์ ๊ฐ์ฒด๋ง์ ์ฌ์ฉํ๋ค๋ ์ ์์ IoC์ ๊ฐ๋ ๊ณผ ์ ๋ค์ด๋ง๋๋ค.
์์กด๊ด๊ณ ๊ฒ์ (Dependency Lookup = DL)
๊ฐ์ฒด ์ค์ค๋ก ๊ฒ์ํ์ฌ ์์กด๊ด๊ณ๋ฅผ ๋งบ๋ ๊ฒ.
์์กด๊ด๊ณ๋ฅผ ๋งบ์ ๋์ ๊ฐ์ฒด๋ฅผ ๊ฒฐ์ ํ๊ณ ์์ฑํ๋ ์์ ์ ์ธ๋ถ ์ปจํ ์ด๋์๊ฒ IoC๋ก ๋งก๊ธฐ์ง๋ง, ์ด๋ฅผ ๊ฐ์ ธ์ฌ ๋๋ ๋ฉ์๋๋ ์์ฑ์๋ฅผ ํตํ ์ฃผ์ ๋์ ์ค์ค๋ก ์ปจํ ์ด๋์๊ฒ ์์ฒญํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ค.
์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ ์์กด๊ด๊ณ ๊ฒ์์ ํด๋น๋๋ค. ์ ์ด๋ ์์กด๊ด๊ณ๋ฅผ ๊ฐ์ ธ์ฌ ๋๋ ์ค์ค๋ก ์์ฒญํ๋ ๊ฒ์ด๋ค.
public UserDao() {
DaoFactory df = new DaoFactory();
this.connectionMaker = df.connectionMaker();
}
DaoFactory
๋์ ApplicationContext
๊ฐ ์ฐ์๋ค๋ฉด ๋ฏธ๋ฆฌ ์ ํด๋์ ์ด๋ฆ์ ์ ๋ฌํด์ ํด๋น๋๋ ์ค๋ธ์ ํธ๋ฅผ ์ฐพ๊ฒ ๋ ๊ฒ์ด๋ค.
public UserDao() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(DaoFactory.class);
this.connectionMaker = context.getBean("connectionMaker", ConnectionMaker.class);
}
๋ณดํต์ ๊ฒฝ์ฐ ์ผ๋ฐ์ ์ธ ์์กด๊ด๊ณ ์ฃผ์ ์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋จ์ํ๊ณ ๊น๋ํ๋ค. ์์กด๊ด๊ณ ๊ฒ์์ ๊ฒฝ์ฐ ๊ฐ์ฒด ์ฝ๋ ์์ ํฉํ ๋ฆฌ ํด๋์ค๋ Spring API๊ฐ ๋ํ๋๋๋ฐ, ์ด๋ ํด์ผํ ์ผ์ ์ง์คํด์ผํ๋ ๊ฐ์ฒด์๊ฒ ์์ด ๋ฐ๋์งํ์ง ์์ ๊ตฌํ ๋ฐฉ์์ด๋ค.
๊ทธ๋ฌ๋ ์ธ์ ๊ฐ๋ ํ ๋ฒ ์์กด๊ด๊ณ ๊ฒ์ ๋ฐฉ์์ด ๋ฑ์ฅํด์ผํ๊ธฐ๋ ํ๋ค. ์คํํฑ ๋ฉ์๋์ธ main()
์์๋ DI๋ฅผ ํตํด ๊ฐ์ฒด๋ฅผ ์ฃผ์
๋ฐ์ ๋ฐฉ๋ฒ์ด ์๊ธฐ ๋๋ฌธ์ด๋ค.
์๋ฒ์๋ main()
๋ฉ์๋๋ ์์ง๋ง, ์ด์ ๋น์ทํ๊ฒ ์์ฒญ์ ๋ฐ์ ๋๋ง๋ค ๋น์ทํ ์ญํ ์ ํ๋ ์๋ธ๋ฆฟ์์ ํ ๋ฒ์ฏค DL ํด์ผํ๋, ์๋ธ๋ฆฟ์ ์คํ๋ง์ด ๋ฏธ๋ฆฌ ๋ง๋ค์ด ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ ์ง์ ๊ตฌํํ ํ์๋ ์๋ค.
๋ ํ ๊ฐ์ง ํน์ง์ผ๋ก๋ DL์์๋ ๊ฒ์์ ํ๋ ์ฃผ์ฒด ๊ฐ์ฒด๋ ๊ผญ ์คํ๋ง ๋น์ผ ํ์๊ฐ ์๋ค๋ ๊ฒ์ด๋ค. DI๋ฅผ ๋ฐ๊ธฐ ์ํด ์คํ๋ง ๋น์ด ๋์ด์ผ ํ์ง๋ง, ์ด์ ๋ ์๋ฌด ๊ด๊ณ๋ ์ฃผ์ ๋ฐ์ง ์๊ณ ์ค์ค๋ก ๊ด๊ณ๋ฅผ ๋งบ๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฐ๋ฉด DI์์๋ ์ฃผ์ ๋ฐ๋ ๊ฐ์ฒด๊ฐ ๋ฐ๋์ ์คํ๋ง ๋น ๊ฐ์ฒด์ฌ์ผํ๋ค.
๊ธฐ๋ฅ ๊ตฌํ์ ๊ตํ
์ผ๋ฐ์ ์ผ๋ก ๊ฐ๋ฐ์ ํ ๋ ๊ฐ๋ฐ์ฉ DB์ ์ด์์ฉ DB๊ฐ ๋ถ๋ฆฌ๋์ด ์ฌ์ฉ๋๋ค. ๊ทธ๋ฐ๋ฐ DI๋ฅผ ํ์ง ์๋๋ค๋ฉด ๋ฐฐํฌํ ๋ DB๋ฅผ ์ฌ์ฉํ๋ ์ฝ๋ ๋ชจ๋ ๋ถ๋ถ์ ๊ณ ๋ คํด์ผํ๋ ์๋ฏธ์๋ ์๊ณ ๊ฐ ํ์ํ๊ณ , ๋ฐฐํฌ ํ ๊ฐ๋ฐ์ฉ ์ฝ๋๋ก ๋๋ฆด ๋๋ ๋ง์ฐฌ๊ฐ์ง์ด๋ค.
ํ์ง๋ง DI๋ฅผ ์ด์ฉํ๋ค๋ฉด ConnectionMaker
์ ์์ํ ํด๋์ค๋ง ํ๋ ๋ฐ๋ก ๋ง๋ค์ด์ฃผ๊ณ DI๋ฅผ ํ๋ ๋ถ๋ถ์์ ์ฌ์ฉํ๋ Dynamic Type๋ง ์ด์ง ๋ฐ๊ฟ์ฃผ๋ฉด ํด๊ฒฐ์ด ๋๋ค.
๋ถ๊ฐ๊ธฐ๋ฅ ์ถ๊ฐ
DB ์ฐ๊ฒฐ ํ์๋ฅผ ์นด์ดํ
ํ๋ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ณ ์ถ์ ๋๋ ์ด๋ป๊ฒ ํ๋ฉด ๋ ๊น? ๋ชจ๋ DAO์ getConnection()
๋ฉ์๋์ ์นด์ดํ
๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ค๋ฉด ์์ฒญ๋ ๋
ธ๋ ฅ์ด ํ์ํ ๊ฒ์ด๋ค.
๊ทธ๋ฌ๋ DI๋ฅผ ์ด์ฉํ๋ฉด ์์ฃผ ๊ฐ๋จํด์ง๋ค. Dao
์ ConnectionMaker
์ฌ์ด์ CountingConnectionMaker
์ ํ๋ ๋ ์ถ๊ฐํ๋ฉด ๋๋ค. ๋ฌผ๋ก ์ด ๋, CountingConnectionMaker
์ getConnection
๋ถ๋ถ์ ์ง์ ๊ตฌํํ๊ฒ ํ๋ฉด ์ ๋๋ค. ์ด ๋ํ ConnectionMaker
์ ์ฃผ์
๋ฐ๋๋ก ๋ง๋ค์ด CountingConnectionMaker
๊ฐ ๋ณธ์
์ธ ์ฐ๊ฒฐ ํ์๋ฅผ ์นด์ดํ
ํ๋๋ฐ๋ง ์ง์คํ ์ ์๋๋ก ๋ง๋ค์ด์ค์ผ ํ๋ค.
public class CountingConnectionMaker implements ConnectionMaker {
int cnt = 0;
private ConnectionMaker realCM;
public CountingConnectionMaker(ConnectionMaker realCM) {
this.realCM = realCM; // CM์ DI๋ฐ๊ฒ ๋ง๋ค์ด ์นด์ดํ
์๋ง ์ง์คํ๋ค!
}
public Connection getConnection() throws ClassNotFoundException, SQLException {
++this.cnt; // ์ฌ๊ธฐ์๋ง ์ง์ค
return realCM.getConnection(); // ์ค์ ์ฐ๊ฒฐ์ ๋ง๋๋๊ฑด ์ฃผ์
๋ฐ์ ๊ฐ์ฒด๊ฐ
}
}
๊ฒฐ๊ณผ์ ์ผ๋ก ๋ค์๊ณผ ๊ฐ์ ์์กด๊ด๊ณ๊ฐ ์์ฑ๋๋ค.
Dao
๋ฅผ ์์ฑํ๋ ๋ถ๋ถ๋ DI๋ฅผ ์ฃผ์
ํ๋ ๋ถ๋ถ๋ง ๋ฐ๊พธ๋ฉด ๋๋ค.
@Configuration
public class CountingDaoFactory {
@Bean
public UserDao userDao() {
return new UserDao(connectionMaker()); // DAO ์ค์ ๋ถ๋ถ์ ๋ฐ๋์ง ์์๋ค
}
@Bean
public ConnectionMaker connectionMaker() {
return new CountingConnectionMaker(realConnectionMaker()); // DI ๋ฐ๋ ๋ถ๋ถ๋ง ๋ฐ๋์๋ค
}
@Bean
public ConnectionMaker realConnectionMaker() {
return new MyConnectionMaker();
}
}
์์ฑ์
Setter ๋ฉ์๋ (์์ ์)
์ผ๋ฐ ๋ฉ์๋
Setter ๋ฉ์๋์ ๊ฒฝ์ฐ ์ด๋ฆ์ด set์ผ๋ก ์์ํด์ผํ๊ณ ํ ๊ฐ์ ํ๋ผ๋ฏธํฐ ๋ฐ์ ๊ฐ์ง์ง ๋ชปํ๋ค๋ ์ ์ฝ์ด ์๋ค.
๋ฐ๋ฉด ์ผ๋ฐ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ์ฌ๋ฌ๊ฐ์ ํ๋ผ๋ฏธํฐ๋ฅผ ํ๊บผ๋ฒ์ ๋ฐ์ ์ ์๊ณ Overloading์ ํตํด ๋ค์ํ๊ฒ ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐ์์ฌ ์ ์๋ค๋ ์ฅ์ ์ด ์๋ค.
XML์ ํตํด์๋ DI๋ฅผ ๊ตฌ์ฑํ ์ ์๋ค. ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ DI ์ ๋ณด๋ฅผ ์์ ํ๋ ๊ฒฝ์ฐ ์ฌ๋น๋๋ฅผ ํด์ผํ์ง๋ง, XML์ ์ด์ฉํ๋ฉด ๊ทธ๋ด ํ์๊ฐ ์๋ค.
<beans>
<bean id="myConnectionMaker" class="<pakage>.MyConnectionMaker" />
<bean id="yourConnectionMaker" class="<pakage>.YourConnectionMaker" />
<bean id="productionConnectionMaker" class="<pakage>.ProductionConnectionMaker" />
<bean id="userDao" class="<pakage>.UserDao">
<property name="connectionMaker" ref="myConnectionMaker" />
</bean>
</beans>
<beans>
: @Configuration
์ญํ / XML์ ๋ฃจํธ ์๋ฆฌ๋จผํธ / ์์ ์ฌ๋ฌ ๊ฐ์ <bean>
์ ์ ์ํ ์ ์๋ค.<bean>
: @Bean
์ญํ ย | Java Code | XML |
---|---|---|
๋น ์ค์ ํ์ผ | @Configuration |
<beans> |
๋น ์ด๋ฆ | @Bean methodName() |
<bean id="methodName" |
๋น ํด๋์ค | return new BeanClass() |
class="package.BeanClass"> |
Setter DI | dao.setValue(value) |
<property name="value" ref="value" /> |
DTD, ์คํค๋ง
XML ๋ฌธ์๋ ๋ฏธ๋ฆฌ ์ ํด์ง ๊ตฌ์กฐ์ ๋ฐ๋ผ ์์ฑ๋๋์ง ๊ฒ์ฌํ ์ ์๋๋ฐ, ์ด๋ฌํ ์ ์ ๋ฐฉ๋ฒ์๋ DTD์ ์คํค๋ง๊ฐ ์๋ค.
DTD (Document Type Definition)
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
ํ์ง๋ง ์คํ๋ง์์ ํน๋ณํ ๋ชฉ์ ์ ์ํด ๋ณ๋์ ํ๊ทธ๋ฅผ ์ฌ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋๋ฐ, ์ด๋ฐ ๋๊ทธ๋ค์ ๋ณ๊ฐ์ ์คํค๋ง ํ์ผ์ ์ ์๋์ด ์๊ณ ๋ ๋ฆฝ์ ์ธ ๋ค์์คํ์ด์ค๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค. ๋ฐ๋ผ์ ์ด๋ฐ ํ๊ทธ๋ค์ ์ฌ์ฉํ๋ ค๋ฉด DTD ๋์ ๋ค์์คํ์ด์ค ๊ธฐ๋ฅ์ด ์๋ ์คํค๋ง๋ฅผ ์ฌ์ฉํด์ผ ํ๊ณ , ์ด๋ฐ ์ด์ ๋ก ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์คํค๋ง๋ฅผ ์ฐ๋ ๊ฒ์ด ๋ฐ๋์งํ๋ค.
์คํค๋ง (Schema)
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"></beans>
<beans>
ํ๊ทธ๋ฅผ ๊ธฐ๋ณธ ๋ค์์คํ์ด์ค๋ก ํ๋ ์คํค๋ง ์ ์ธ์ด๋ค.
DaoFactory
๋์ XML ์ค์ ์ ๋ณด๋ฅผ ํ์ฉํ๋ ค๋ฉด GenericXmlApplicationContext
๋ฅผ ์ฌ์ฉํ๋ค.
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="connectionMaker" class="package.MyConnectionMaker" />
<bean id="userDao" class="springbook.user.dao.UserDao">
<property name="connectionMaker" ref="connectionMaker" />
</bean>
</beans>
GenericXmlApplicationContext
ApplicationContext context = new GenericXmlApplicationContext("applicationContext.xml");
ClassPathXmlApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext("daoContext.xml", UserDao.class)
๊ฒฝ๋ก ์ ๋ณด ๊ธฐ์ค์ UserDao
๋ก ์ค์ ํ ์ ์์ด ์ข ๋ ํธํ๊ฒ xml ํ์ผ ์์น๋ฅผ ์ ์ ์ ์๋ค.
Java์๋ ์ฐ๋ฆฌ๊ฐ ์ง์ ๊ตฌํํ ConnectionMaker
๊ณผ ๊ฐ์ด DB ์ปค๋ฅ์
์ ๊ฐ์ ธ์ค๋ ๊ธฐ๋ฅ์ ์ถ์ํ ํ DataSource
์ธํฐํ์ด์ค๊ฐ ์๋ค.
๊ทธ๋ฌ๋ DataSource
์๋ getConnection()
์ธ์๋ ๋ง์ ๋ฉ์๋๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ง์ ๊ตฌํํ๊ธฐ์๋ ๋ถ๋ด์ด ์๋๋ฐ, ๋คํํ๋ ์ด๋ฏธ ๊ตฌํ๋์ด ์๋ ํด๋์ค๊ฐ ์์ผ๋ ๊ทธ๊ฑธ ์ฐ๋ฉด ๋๋ค.
@Setter
public class Dao {
private DataSource dataSource;
public void add(User user) throws SQLException {
Connection con = dataSource.getConnection();
// ...
}
}
DataSource
๋ฅผ DI ๋ฐ์์ ๋๋ ์์ ๊ฐ์ด ConnectionMaker
์ ์ฌ์ฉํ๋ฏ์ด ์ฐ๋ฉด ๋๋ค.
@Bean
public DataSource dataSource() {
SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
dataSource.setDriverClass(com.mysql.cj.jdbc.Driver.class);
dataSource.setUrl("jdbc:mysql://localhost/spring?serverTimezone=UTC");
dataSource.setUsername("user name");
dataSource.setPassword("password");
return dataSource;
}
@Bean
public Dao dao() {
Dao dao = new Dao();
dao.setDataSource(dataSource());
return dao
}
DaoFactory
์ ๊ฒฝ์ฐ ํ
์คํธ ํ๊ฒฝ์์ ๊ฐ๋จํ๊ฒ ์ฌ์ฉํ ์ ์๋ SimpleDriverDataSource
๋ฅผ ์ฌ์ฉํด์ ๊ตฌํํ์๋ค.
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource" />
์๋ก ์๊ธด dataSource()
๋น๋ XML์ ์ถ๊ฐํด์ค์ผํ๋ค.
์์ ๊ฐ์ด ํ์ ๋ XML์์ dataSource()
์์ ์ฌ์ฉํ SimpleDriverDataSource
์ Setter๋ก ๋ฃ์ด์ค ์ ์ ์ ๋ณด๋ ๋ค์ด์์ง ์๋ค๋ ๋ฌธ์ ์ ์ด ์๊ธด๋ค.
UserDao
์ ๊ฐ์ด ๋ค๋ฅธ ๋น์ ์์กดํ๋ ๊ฒฝ์ฐ <property>
ํ๊ทธ์ ref
์์ฑ์ผ๋ก ์์กดํ ๋น ์ด๋ฆ์ ๋ฃ์ด์ฃผ๋ฉด ๋์ง๋ง, SimpleDriverDataSource
์ ๊ฒฝ์ฐ Setter๊ฐ ์ผ๋ฐ ํด๋์ค๋ ์คํธ๋ง ๊ฐ์ด ํ๋ผ๋ฏธํฐ๋ก ๋ค์ด์ค๋๋ฐ ์ด๋ป๊ฒ ํด์ผํ ๊น?
๋คํํ๋ Setter์๋ ๋น ๋ฟ๋ง ์๋๋ผ ๊ฐ์ฒด, ์คํธ๋ง ๋ฑ์ ๊ฐ๋ ๋ฃ์ด์ค ์ ์๋ค. ์ฆ, ๋ค๋ฅธ ๋น ๊ฐ์ฒด์ ๋ ํผ๋ฐ์ค๊ฐ ์๋์ด๋ Setter๋ก ๋ฐ์ดํฐ๋ฅผ ์ค ์ ์๋ค๋ ์๋ฆฌ์ด๋ค. ํ์ง๋ง ์ด ๋๋ DI์ฒ๋ผ Dynamic Type์ ์์ ์์ฌ๋ก ๋ฐ๊พธ๋ ค๋ ๋ชฉ์ ์ด ์๋ ๋จ์ํ ๊ฐ์ ์ฃผ์ ํ๋ ค๋ ๋ชฉ์ ์ด๋ค. ์ด๊ฒ๋ ์ผ์ข ์ DI๋ผ๊ณ ๋ณผ ์ ์๋๋ฐ, Dynamicํ๊ฒ ์ ๋ณด๊ฐ ๋ฐ๋์ง ์์ง๋ง, ๊ฐ์ฒด์ ํน์ฑ์ ์ธ๋ถ์์ ๋ณ๊ฒฝํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
<property name="driverClass" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/spring?serverTimezone=UTC" />
<property name="username" value="user name" />
<property name="password" value="password" />
์ด์ ๊ฐ์ด ํ๋์ฝ๋ฉ ๋์ด์๋ ์ค์ ์ ๋ณด๋ฅผ XML์ ํตํด ์ฃผ์ ํด์ค ์ ์๊ฒ ๋๋ค.
์ฆ, DataFactory
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์๋ XML์ ์ฌ์ฉํด์ DI๋ฅผ ํ ์ ์๋ ๊ฒ์ด๋ค.
์ด ๋ ์คํ๋ง์ด ํ๋กํผํฐ ๊ฐ์ Setter ๋ฉ์๋์ ํ๋ผ๋ฏธํฐ ํ์
์ผ๋ก ์๋ ํ๋ณํ ํด์ฃผ๊ธฐ ๋๋ฌธ์ "com.mysql.cj.jdbc.Driver"
์ ๊ฐ์ด ๊ฐ์ฒด๋ฅผ String์ผ๋ก ์ค๋ ๋๋ ๊ฒ์ด๋ค.
์ค์ ๋ด๋ถ์์๋ ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๊ฐ ๋ณํ๋๋ค.
Class driverClass = Class.forName("com.mysql.cj.jdbc.Driver"); dataSource.setDriverClass(driverClass);
์ด ๋ ํ๋ณํ์ Class, URL, File, Charset, List, Map, Set, Properties ๋ฑ๋ฑ์ผ๋ก๋ ๊ฐ๋ฅํ๋ค.
ICPC 2019 Seoul Regional์ ์ค๋นํ๋ฉฐ ๋ง๋ ํ ๋ ธํธ์ ๋๋ค.
koosaga๋์ ๋น๋กฏํ DeobureoMinkyuParty ํ ๋ ธํธ๋ฅผ ์ฐธ๊ณ ํ๋ฉฐ ๋ง๋ค์์ต๋๋ค.
/*
1. build level graph with bfs ( O(E) )
2. flow blocking flow in level graph ( O(VE) )
In each step, level grows at least by 1, and eventually grows upto O(V)
So total time complexity is O(V^2E)
*/
struct dinic { // O(V^2 E)
struct edg { int v, c, r; };
int n;
vi dis, itr;
vector<vector<edg>> g;
dinic(int n) : n(n), g(n, vector<edg>()), dis(n), itr(n) { }
void addedge(int u, int v, int c) {
g[u].pb({ v, c, sz(g[v]) });
g[v].pb({ u, 0, sz(g[u])-1 });
}
bool bfs(int s, int t) { // build level graph
dis.assign(n, 0), itr.assign(n, 0);
queue<int> q;
q.push(s);
dis[s] = 1;
while(q.size()) {
int u = q.front(); q.pop();
for(auto& [v, c, r] : g[u]) {
if(c > 0 && !dis[v]) {
dis[v] = dis[u] + 1;
q.push(v);
}
}
}
return dis[t] > 0;
}
int dfs(int u, int t, int f) { // get blocking flow
if(u == t) return f;
for( ; itr[u] < g[u].size(); itr[u]++) {
auto& [v, c, r] = g[u][itr[u]];
if(c > 0 && dis[v] == dis[u] + 1) {
int w = dfs(v, t, min(f, c));
if(w) {
g[u][itr[u]].c -= w;
g[v][r].c += w;
return w;
}
}
}
return 0;
}
i64 nflow(int s, int t) { // network flow
i64 ret = 0;
while(bfs(s, t)) {
int r;
while(r = dfs(s, t, 2e9)) ret += r, debug("-----");
}
return ret;
}
};
/*
- alternating path: path consists of (x, y, x, y, x) for X = { x | x is matched edge}, Y = { y | y is not mathed edge }
- augmenting path: path consists of (y, x, y, x, y)
- if augmenting path exists, we can match one more edge with fliping matched state (x, y, x, y, x)
For maximaum matching A, B
1. lv[0] = { v | v in A and v is not matched }
2. starting from lv[0] vertices, get alternating path with bfs
3. starting from lv[0] vertices, get augmenting path with dfs
* min cover: selecting minimum vertices to cover all edges
* max independent set: selecting maximum vertices not connected with edge
* V - min cover = max independent set
*/
struct hofcroft {
int n, m;
vi dis, l, r, vis, chk;
vvi g;
hofcroft(int n, int m) : n(n), m(m), g(n, vi()) { }
void addedge(int u, int v) { g[u].pb(v); }
bool bfs() { // build alternating path starts from lv[0] nodes
queue<int> q;
bool ok = 0;
dis.assign(n, 0);
FOR(u, 0, n) {
if(l[u] == -1 && !dis[u]) {
q.push(u);
dis[u] = 1;
}
}
while(q.size()) {
int u = q.front(); q.pop();
for(int v : g[u]) {
if(r[v] == -1) ok = 1; // v is not matched
else if(!dis[r[v]]) { // if v is matched, u>v>r[v] can be path
dis[r[v]] = dis[u] + 1;
q.push(r[v]);
}
}
}
return ok;
}
bool dfs(int u) { // find augmenting path and flip it!
if(vis[u]) return 0; // augmenting path start/end with non-matched vertices
vis[u] = 1;
for(int v : g[u]) {
if(r[v] == -1 || (dis[r[v]] == dis[u] + 1 && dfs(r[v]))) {
l[u] = v; r[v] = u;
return 1;
}
}
return 0;
}
int match() { // bipartite match
l.assign(n, -1);
r.assign(m, -1);
int ret = 0;
while(bfs()) {
vis.assign(n, 0);
FOR(u, 0, n) if(l[u] == -1 && dfs(u)) ++ret;
}
return ret;
}
void rdfs(int u) { // dfs matched
if(chk[u]) return;
chk[u] = 1;
for(int v : g[u]) {
chk[v + n] = 1;
rdfs(r[v]);
}
}
vi getcover() { // get min cover vertices
match();
chk.assign(n+m, 0);
FOR(u, 0, n) if(l[u] == -1) rdfs(u);
vi ret;
FOR(u, 0, n) if(!chk[u]) ret.pb(u);
FOR(u, n, n+m) if(chk[u]) ret.pb(u);
return ret;
}
};
const int WINF = 0x3fffffff, FINF = 0x3fffffff; // weight/flow inf
struct mcmf {
struct edg { int v, c, r, w; };
int n;
vi dis, par, peg;
vector<bool> inq;
vector<vector<edg> > g;
mcmf(int n) : n(n), g(n, vector<edg>()), par(n), peg(n) { }
void addedge(int u, int v, int c, int w) {
g[u].pb({ v, c, sz(g[v]), w });
g[v].pb({ u, 0, sz(g[u])-1, -w });
}
bool spfa(int s, int t) {
dis.assign(n, WINF);
inq.assign(n, 0);
queue<int> q;
dis[s] = 0;
inq[s] = 1;
q.push(s);
bool ok = 0;
while(q.size()) {
int u = q.front(); q.pop();
if(u == t) ok = 1;
inq[u] = 0;
FOR(eidx, 0, g[u].size()) {
auto [v, c, r, w] = g[u][eidx];
if(c > 0 && dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
par[v] = u;
peg[v] = eidx;
if(!inq[v]) {
inq[v] = 1;
q.push(v);
}
}
}
}
return ok;
}
ii flow(int s, int t) { // return (max_flow, min_cost)
int cost = 0, flow = 0;
while(spfa(s, t)) {
int cur = FINF;
for(int u = t; u != s; u = par[u]) cur = min(cur, g[par[u]][peg[u]].c);
for(int u = t; u != s; u = par[u]) {
int r = g[par[u]][peg[u]].r;
g[par[u]][peg[u]].c -= cur;
g[u][r].c += cur;
}
flow += cur;
cost += dis[t] * cur;
}
return { flow, cost };
}
};
// Caution: All vertices' idx > 0 (par[S] = 0)
const int MAXND = 500, S = 1, T = 2, INF = 0x3fffffff;
struct nflow {
struct edge {
edge* rev;
int v, c, f; // need initialized
edge(int v, int c) : v(v), c(c), f(0) { }
int res() { return c-f; }
int flow(int x) { f += x, rev->f -= x; }
};
vector<edge*> g[MAXND];
int par[MAXND];
edge* pedg[MAXND];
nflow() {
FOR(i, 0, MAXN) g[i] = vector<edge*>();
}
void addedge(int u, int v, int c) {
edge *uv = new edge(v, c), *vu = new edge(u, 0);
uv->rev = vu, vu->rev = uv;
g[u].pb(uv), g[v].pb(vu);
}
i64 maxflow() {
i64 ret = 0;
while(true) {
memset(par, 0, sizeof(par));
queue<int> q;
q.push(S);
par[S] = S;
while(q.size()) {
int u = q.front(); q.pop();
for(auto e : g[u]) {
if(e->res() && !par[e->v]) {
q.push(e->v);
par[e->v] = u;
pedg[e->v] = e;
if(e->v == T) break;
}
}
if(par[T]) break;
}
if(!par[T]) break;
int flow = INF;
for(int u = T; u != S; u = par[u]) flow = min(flow, pedg[u]->res());
for(int u = T; u != S; u = par[u]) pedg[u]->flow(flow);
ret += flow;
}
return ret;
}
};
const int MAXN = 5e2+10;
int vis[MAXN], ato[MAXN], bto[MAXN];
bool dfs(int u) {
if(vis[u]) return 0;
vis[u] = 1;
for(int v : g[u]) {
if(bto[v] == -1 || dfs(bto[v])) {
ato[u] = v;
bto[v] = u;
return 1;
}
}
return 0;
}
int bimatch() {
memset(ato, -1, sizeof(ato)), memset(bto, -1, sizeof(bto));
int ret = 0;
FOR(u, 0, n) {
memset(vis, 0, sizeof(vis));
if(dfs(u)) ++ret;
}
return ret;
}
/*
2-SAT: (A || B) && (C || D) && (E || F) ...
1. X || Y = !X -> Y, !Y -> X (Proposition)
False: T -> F, True: Others
2. !X, X in same SCC: no solution
3. For every SCC, each node in same SCC must have same flag (if both T, F exists in same SCC, T->F exists)
4. Assign False to (Don't have in edge & Unassigned node) and erase node
- sort nodes topologically, iterate nodes with assigning False to var if var is unassigned
- !X node: X = True, X node: X = False
*/
struct sat2 {
struct tarjan {
int n, ncnt, scnt;
vi scc, dis;
vvi g;
stack<int> sta;
tarjan(int n) : n(n), g(n, vi()) { } // n: number of variables (NOT NODES!)
void addedge(int u, int v) { g[u].pb(v); } // directed graph
int f(int u) {
int ret = dis[u] = ncnt++;
sta.push(u);
for(int v : g[u]) {
if(dis[v] == -1) ret = min(ret, f(v));
else if(scc[v] == -1) ret = min(ret, dis[v]);
}
if(ret == dis[u]) {
while(1) {
int t = sta.top(); sta.pop();
scc[t] = scnt;
if(t == u) break;
}
++scnt;
}
return ret;
}
vi& get_scc() {
ncnt = scnt = 0;
scc = dis = vi(n, -1);
sta = stack<int>();
FOR(i, 0, n) if(dis[i] == -1) f(i);
dis.clear();
return scc;
}
};
int n;
vi res;
tarjan tj;
sat2(int n) : n(n), tj(2*n) { }
int nd(int u, int neg) { return u + neg*n; } // var u's node
int neg(int u) { return (u+n)%(2*n); } // ~u
void addedge(int u, int nu, int v, int nv) { // add (X || Y) clauses
u = nd(u, nu), v = nd(v, nv);
tj.addedge(neg(u), v);
tj.addedge(neg(v), u);
}
vi& solve() { // return solved vars, if no solution return vi()
vi& scc = tj.get_scc();
FOR(u, 0, n) if(scc[u] == scc[u+n]) return res;
res.assign(n, -1);
vi ord(2*n);
FOR(i, 0, 2*n) ord[i] = i;
sort(ALL(ord), [&](int u, int v) { return scc[u] > scc[v]; });
FOR(i, 0, 2*n) {
int u = ord[i];
if(res[u%n] == -1) res[u%n] = !(u<n);
}
return res;
}
};
// Find Bridge
const int MAXN = 3e5, INF = 0x7fffffff;
int ncnt, vid[MAXN], par[MAXN];
vii g[MAXN]; // (v, bridge flag)
int dfs(int u) {
int ret = vid[u] = ncnt++;
for(auto& e : g[u]) {
int v = e.se, c = INF;
if(par[u] == v) continue; // Tree edge
if(vid[v] == -1) { // Tree Edge
par[v] = u;
c = min(c, dfs(v));
} else c = min({ c, vid[v], vid[u] }); // Forward/Backward Edge
if(c > vid[u]) e.fi = 1; // Bridge
ret = min(ret, c);
}
return ret;
}
string s, t;
vi getpi(const string& str) {
int n = str.size(), len = 0;
vi pi(n, 0);
FOR(i, 1, n) {
while(len && str[len] != str[i]) len = pi[len-1];
if(str[len] == str[i]) pi[i] = ++len;
}
return pi;
}
vi kmp() {
int n = s.size(), m = t.size(), len = 0;
vi ret, pi = getpi(t);
FOR(i, 0, n) {
while(len && s[i] != t[len]) len = pi[len-1];
if(s[i] == t[len] && ++len == m) ret.pb(i-len+1), len = pi[len-1];
}
return ret;
}
// f(p) = s[0] + s[1] * p + s[2] * p^2 + ... + s[n-1] * p^{n-1)
// h[i+1] = p * (h[i] - s[i] * s^(m-1)) + s[i+m]
// --> sub first character from hash > hash degree up > add last character to hash
const i64 MUL = 232153, MD = 1012924417; // be careful for MD not MOD
void mod(i64& x) { x %= MD; if(x < 0) x += MD; }
vi rabin(string& s, string& t) { // return start indexs
vi ret;
i64 ht = 0, hs = 0, mul = 1;
RFOR(i, sz(t)-1, 0) { // get t's hash
mod(ht += mul * t[i] % MD);
mod(mul = mul * MUL);
}
mul = 1;
RFOR(i, sz(t)-1, 0) { // get s's hash for first sz(t) string
mod(hs += mul * s[i] % MD);
if(i != 0) mod(mul = mul * MUL); // mul must be p^{m-1)
}
if(hs == ht) ret.pb(0);
FOR(i, sz(t), sz(s)) {
mod(hs -= mul*s[i-sz(t)]%MD);
mod(hs *= MUL);
mod(hs += s[i]);
if(hs == ht) ret.pb(i-sz(t)+1);
}
return ret;
}
const int MAX_NODE = 1e6+10;
int cld[MAX_NODE][30];
i64 cnt[MAX_NODE];
int ncnt = 1;
void push(const string& x) {
int u = 0;
FOR(i, 0, x.size()) {
if(!cld[u][x[i]-'a']) cld[u][x[i]-'a'] = ncnt++;
u = cld[u][x[i]-'a'];
}
++cnt[u];
}
void calc_back(int u) {
FOR(i, 0, 30) {
if(cld[u][i]) {
calc_back(cld[u][i]);
cnt[u] += cnt[cld[u][i]];
}
}
cnt[u] %= MOD;
}
const int MAXNODE = 1e5+10, MAXC = 26, INITCHAR = 'a';
struct ahocorasick {
int ncnt, t[MAXNODE][MAXC], f[MAXNODE], chk[MAXNODE];
ahocorasick() : ncnt(0) { memset(t, 0, sizeof(t)), memset(f, 0, sizeof(f)), memset(chk, 0, sizeof(chk)); }
void insert(const string& s) {
int u = 0;
for(auto i : s) {
i -= INITCHAR;
if(!t[u][i]) t[u][i] = ++ncnt;
u = t[u][i];
}
chk[u] = 1; // end of string
}
void precalc() {
queue<int> q;
FOR(i, 0, MAXC) if(t[0][i]) q.push(t[0][i]);
// calculdate fail, chk with bfs
while(q.size()) {
int x = q.front(); q.pop();
FOR(i, 0, MAXC) {
if(t[x][i]) {
int u = x, p = f[u];
while(p && !t[p][i]) p = f[p]; // find fail link
u = t[u][i], p = t[p][i]; // goto original target node
f[u] = p;
if(chk[p]) chk[u] = 1;
q.push(u);
}
}
}
}
bool query(const string& s) {
int u = 0;
for(auto i : s) {
i -= INITCHAR;
while(u && !t[u][i]) u = f[u];
if(chk[u = t[u][i]]) return true;
}
return false;
}
};
/*
sa[i] = ordered suffix (suffix's start position)
ord[i] = [i:]'s index in sa (ord[sa[i]] = i)
lcp[i] = longest common prefix length of two suffix [i-1:], [i:]
LCP's Lemma
1. Two adjacent in SA suffixes' LCP is always bigger than which of non-adjacents
2. lcp(sa[i-1], sa[i]) = h, h >= 1 then
lcp(sa[i-1]+1, sa[i]+1) = h-1
So that lcp[sa[i]+1] >= h-1 because it is always bigger than lcp(sa[i-1]+1, sa[i]+1) by Lemma 1
and by Lemma 2, lcp(sa[i-1]+1, sa[i]+1) = h-1
*/
struct sfxarray {
int n;
string& str;
vi sa, lcp, ord;
sfxarray(string& str) : str(str), n(str.size()) { }
void getsa() {
sa = ord = vi(n+1);
FOR(i, 0, n) sa[i] = i, ord[i] = str[i]; ord[n] = 0;
for(int t = 1; t <= n; t *= 2) {
int sz = max(257, n+1);
vi cnt, tmp;
cnt = tmp = vi(sz, 0);
FOR(i, 0, n) ++cnt[ord[min(n, i+t)]];
FOR(i, 1, sz) cnt[i] += cnt[i-1];
FOR(i, 0, n) tmp[--cnt[ord[min(n, i+t)]]] = i;
cnt = vi(sz, 0);
FOR(i, 0, n) ++cnt[ord[i]];
FOR(i, 1, sz) cnt[i] += cnt[i-1];
RFOR(i, n-1, 0) sa[--cnt[ord[tmp[i]]]] = tmp[i];
tmp[sa[0]] = 1;
FOR(i, 1, n) {
int u = sa[i-1], v = sa[i];
tmp[v] = tmp[u] + (ord[u] < ord[v] || ord[u+t] < ord[v+t]);
}
ord = tmp;
if(ord[sa[n-1]] == n) break;
}
FOR(i, 0, n) --ord[i];
}
void getlcp() {
lcp = vi(n, 0);
for(int i = 0, len = 0; i < n; ++i, len = max(0, len-1)) {
if(ord[i]) {
for(int j = sa[ord[i]-1]; str[i+len] == str[j+len]; ++len);
lcp[ord[i]] = len;
}
}
}
tuple<vi, vi, vi> build() { getsa(), getlcp(); return { sa, lcp, ord }; }
};
const int MAXN = 1000005;
int aux[2 * MAXN - 1];
void solve(int n, int *str, int *ret){
// *ret : number of nonobvious palindromic character pair
for(int i=0; i<n; i++){
aux[2*i] = str[i];
if(i != n-1) aux[2*i+1] = -1;
}
int p = 0, c = 0;
for(int i=0; i<2*n-1; i++){
int cur = 0;
if(i <= p) cur = min(ret[2 * c - i], p - i);
while(i - cur - 1 >= 0 && i + cur + 1 < 2*n-1 && aux[i-cur-1] == aux[i+cur+1]){
cur++;
}
ret[i] = cur;
if(i + ret[i] > p){
p = i + ret[i];
c = i;
}
}
}
// dependency: GCD(i64 a, i64 b)
struct frac {
i64 a, b;
frac(i64 _a=0, i64 _b=1) : a(_a), b(_b) { if(a == 0 && b == 0) b = 1; assert(b != 0); relax(); }
// Essential: Basic Operations
void relax() { i64 g = GCD(abs(a), abs(b)); a /= g, b /= g; }
frac operator + (const frac &ot) const { return { a * ot.b + ot.a * b, b * ot.b }; }
frac operator - (const frac &ot) const { return { a * ot.b - ot.a * b, b * ot.b }; }
frac operator * (const frac &ot) const { return { a * ot.a, b * ot.b }; }
frac operator / (const frac &ot) const { return { a * ot.b, b * ot.a }; }
frac operator - () { return { -a, b }; }
// Essential: Basic Comparation
bool operator == (const frac& ot) const { return a * ot.b == ot.a * b; }
bool operator < (const frac& ot) const { return a * ot.b < ot.a * b; }
bool operator <= (const frac& ot) const { return a * ot.b <= ot.a * b; }
bool operator > (const frac& ot) const { return ot <= *this; }
bool operator >= (const frac& ot) const { return ot < *this; }
// Optional: Advanced Operations
const frac& operator += (const frac &ot) { return *this = *this + ot; }
const frac& operator -= (const frac &ot) { return *this = *this - ot; }
const frac& operator *= (const frac &ot) { return *this = *this * ot; }
const frac& operator /= (const frac &ot) { return *this = *this / ot; }
};
// fraction IO
ostream& operator<< (ostream& os, const frac& frac_x) { return os << frac_x.a << "/" << frac_x.b; }
istream& operator>> (istream& os, frac& frac_x) {
os >> frac_x.a >> frac_x.b;
frac_x.relax();
return os;
}
// Do not use this class as const
typedef i64 ELEM;
const ELEM MOD = 1e9+7; // If don't use MOD, set as 0x7fffffffffffffff
struct mat {
int n, m;
vector<vector<ELEM> > ar;
// ----- constructor, assignment ----- //
mat(int n, int m, ELEM x = 0) : n(n), m(m), ar(n, vector<ELEM>(m, x)) { }
mat(int n = 0) : mat(n, n) { }
mat(const mat& o) { n = o.n, m = o.m, ar = o.ar; }
mat(const vector<vector<ELEM>>& ar) : n(ar.size()), m(ar.size() ? ar[0].size() : 0), ar(ar) { }
// ----- get field ----- //
operator const vector<vector<ELEM> >& () const { return ar; }
vector<ELEM>& operator[](int i) { return ar[i]; }
const vector<ELEM>& operator[](int i) const { return ar[i]; }
// ----- calculate ----- //
mat pow(i64 x) const {
assert(n == m && 0 <= x);
mat a(*this), ret = eye(n);
while(x) {
if(x%2) ret = ret * a;
a = a * a;
x /= 2;
}
return ret;
}
mat operator * (const mat& o) const {
assert(m == o.n);
mat ret(n, o.m);
FOR(i, 0, n) {
FOR(j, 0, o.m) {
FOR(k, 0, m) {
ret[i][j] += ar[i][k] * o[k][j] % MOD;
ret[i][j] %= MOD;
}
}
}
return ret;
}
mat operator + (const mat& o) const {
assert(n == o.n && m == o.m);
mat ret(n, m);
FOR(i, 0, n) FOR(j, 0, n) ret[i][j] = (ar[i][j] + o[i][j]) % MOD;
return ret;
}
mat operator - (const mat& o) const {
assert(n == o.n && m == o.m);
mat ret(n, m);
FOR(i, 0, n) FOR(j, 0, n) ret[i][j] = (ar[i][j] - o[i][j]) % MOD;
return ret;
}
mat operator * (const ELEM x) const {
mat ret = ar;
FOR(i, 0, n) FOR(j, 0, m) ret[i][j] = ret[i][j] * x % MOD;
return ret;
}
mat operator / (const ELEM x) const {
mat ret = ar;
FOR(i, 0, n) FOR(j, 0, m) ret[i][j] = ret[i][j] / x % MOD;
return ret;
}
const mat& operator - () {
FOR(i, 0, n) FOR(j, 0, m) ar[i][j] = -ar[i][j];
return *this;
}
// If use dp matrix as: state = state * dpmat
// use rotated dpmat and horizontal state mat
mat rotate() const {
mat ret(m, n);
FOR(i, 0, n) FOR(j, 0, m) ret[j][i] = ar[i][j];
return ret;
}
static mat eye(const int size) {
mat ret(size);
FOR(i, 0, size) ret[i][i] = 1;
return ret;
}
// return matrix dp
// dp[i] = ar[0] * dp[i-n] + ar[1] * dp[i-n+1] + ... + ar[n-1] * dp[i-1]
// data matrix br: br[0] = dp[i], br[1] = dp[i-1] ...
// If DP equation contains constant, fix one element for constant
static mat dpmat(const vector<ELEM>& ar) {
int n = ar.size();
mat ret(n, n);
FOR(i, 0, n-1) ret[i][i+1] = 1; // transition prev dp values
FOR(i, 0, n) ret[n-1][i] = ar[i]; // DP equation
return ret;
}
};
ostream& operator<<(ostream& os, const mat& v) { for(auto vv : (vector<vector<ELEM> >)v) os << vv << ENDL; return os; }
namespace miller_rabin{ // O(logP)
i64 mul(i64 x, i64 y, i64 mod){ return (__int128) x * y % mod; }
i64 ipow(i64 x, i64 y, i64 p){
i64 ret = 1, piv = x % p;
while(y){
if(y&1) ret = mul(ret, piv, p);
piv = mul(piv, piv, p);
y >>= 1;
}
return ret;
}
bool miller_rabin(i64 x, i64 a){
if(x % a == 0) return 0;
i64 d = x - 1;
while(1){
i64 tmp = ipow(a, d, x);
if(d&1) return (tmp != 1 && tmp != x-1);
else if(tmp == x-1) return 0;
d >>= 1;
}
}
bool isprime(i64 x){
for(auto &i : {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}){
if(x == i) return 1;
if(x > 40 && miller_rabin(x, i)) return 0;
}
if(x <= 40) return 0;
return 1;
}
}
const int RANGE = 2e7;
int pn, spf[RANGE], pr[RANGE]; // spf[x] = min prime factor of x
void eulerSieve() {
FOR(x, 2, RANGE) {
if(!spf[x]) spf[x] = pr[pn++] = x; // if x is prime, spf[x] = x
for(int j = 0; x*pr[j] < RANGE; ++j) {
spf[x*pr[j]] = pr[j];
if(x % pr[j] == 0) break;
}
}
}
// 1. c[n][r] = c[n-1][r-1] + c[n-1][r]
// 2. nCr = n! / ((n-r)! * r!)
const int RANGE = 1e6;
// precalc: n!, n!^(-1) --> O(NlogP)
void precalc_1() {
i64 ftr[RANGE], iftr[RANGE];
ftr[0] = iftr[0] = 1;
FOR(i, 1, RANGE) {
ft[i] = (ft[i-1] * (i64)i) % MOD;
ift[i] = (ift[i-1] * POW((i64)i, MOD-2)) % MOD;
}
}
// (n-1)!^(-1) = n*n!^(-1) --> O(n+logP)
void precalc_2() {
i64 ftr[RANGE], iftr[RANGE];
ftr[0] = iftr[0] = 1;
FOR(i, 1, RANGE) ftr[i] = ftr[i-1] * i;
iftr[RANGE-1] = POW(ftr[RANGE-1], MOD-2);
RFOR(i, RANGE-2, 0) iftr[i] = (i * iftr[i+1]) % MOD;
}
// inv(1) = 1, inv(1) = -floor(p/i) + inv(p%i) --> O(n)
void precalc_3() {
i64 inv[RANGE+1], ftr[RANGE], iftr[RANGE];
inv[1] = 1;
FOR(i, 2, RANGE+1) {
inv[i] = inv[MOD % i] - (MOD / i);
if(inv[i] < 0) inv[i] += MOD;
inv[i] %= MOD;
}
}
FOR(i, 1, (1 << n)) { // get Union(A, B, C, D ...)
int bits = __builtin_popcount(i);
if(bits % 2); // add to ans
else; // sub to ans
}
/* O(n)
f(n, k) = last survived person for n-people, k-cycle
< basic idea >
except 1 element from f(n, k), then answer is f(n-1, k)
but f(n-1, k) need to be repositioned to starting from kth's next person
< 1-indexed >
f(1, k) = 1
f(n, k) = ((f(n-1, k) + k-1) % n) + 1
< 0-indexed >
f(1, k) = 0;
f(n, k) = ((f(n-1, k) + k) % n)
*/
// O(KlogN) algorithm
long long joseph (long long n,long long k) {
if (n==1LL) return 0LL;
if (k==1LL) return n-1LL;
if (k>n) return (joseph(n-1LL,k)+k)%n;
long long cnt=n/k;
long long res=joseph(n-cnt,k);
res-=n%k;
if (res<0LL) res+=n;
else res+=res/(k-1LL);
return res;
}
struct fenwick {
int n;
vector<i64> t;
void init(int _n) { n = _n, t = vector<i64>(n+1, 0); }
void add(int u, i64 x) { for(++u; u < t.size(); u += (u&-u)) t[u] += x; }
i64 sum(int u) {
i64 ret = 0;
for(++u; u > 0; u -= (u&-u)) ret += t[u];
return ret;
}
i64 operator[](int u) {
++u;
int ret = t[u], p = u - (u&-u);
--u;
while(u != p) {
ret -= t[u];
u -= (u&-u);
}
return ret;
}
// Can use all elements are positive.
// k >= 0
// find x which ( sum[a[0]..a[x-1]] < k <= sum[a[0]..a[x]] )
int lower(i64 k) {
if(k < 0) return 0;
int l = (1 << (8*sizeof(int) - __builtin_clz(n)) - 1);
int u = 0;
while(l > 0 && u <= n) {
int tu = u + l;
if(k > t[tu]) u = tu, k -= t[tu];
do l >>= 1;
while(l > 0 && u + l > n);
}
return u;
}
// find x which ( sum[a[0]..a[x-1]] <= k < sum[a[0]..a[x]] )
int upper(i64 k) {
if(k < 0) return 0;
int l = (1 << (8*sizeof(int) - __builtin_clz(n)) - 1);
int u = 0;
while(l > 0 && u <= n) {
int tu = u + l;
if(k >= t[tu]) u = tu, k -= t[tu];
do l >>= 1;
while(l > 0 && u + l > n);
}
return u;
}
};
struct segtree {
vi ys[RANGE], t[RANGE];
// Notify segtree update access on (x, y)
void initpos(int x, int y) {
for(++x; x < RANGE; x += (x&-x)) {
ys[x].pb(y);
}
}
// Execute after notifying (x, y)
void init() {
FOR(i, 0, RANGE) sort(ALL(ys[i])), UNIQUE(ys[i]), t[i].assign(ys[i].size()+1, 0);
}
// add (x, y) to c
void add(int x, int y, int c) {
for(++x; x < RANGE; x += (x&-x)) {
for(int j = getidx(ys[x], y)+1; j < sz(t[x]); j += (j&-j)) {
t[x][j] += c;
}
}
}
// partial sum of ([..x], [..y])
int sum(int x, int y) {
int ret = 0;
for(++x; x > 0; x -= (x&-x)) {
int j = getidx(ys[x], y);
if(j == ys[x].size() || ys[x][j] > y) --j;
for(++j; j > 0; j -= (j&-j)) {
ret += t[x][j];
}
}
return ret;
}
} seg;
struct rfenwick { // using 2 basic fenwick tree
fenwick ax, b;
void init(int n) { ax.init(n), b.init(n); }
void add(int u, i64 x) { b.add(u, x); }
void add(int s, int e, i64 x) {
ax.add(s, x);
ax.add(e+1, -x);
b.add(s, -x * (s-1));
b.add(e+1, x * e);
}
i64 sum(int u) {
return u * ax.sum(u) + b.sum(u);
}
};
template<class T, class C>
struct segtree {
int n;
vector<T> t;
void build(const vector<T>& ar) {
n = ar.size();
t.assign(n*2, 0);
FOR(i, 0, n) t[n+i] = ar[i];
RFOR(i, n-1, 1) t[i] = C()(t[i*2], t[i*2+1]);
}
void mod(int u, T k) {
for(t[u += n] = k; u > 1; u /= 2) t[u/2] = C()(t[u], t[u^1]);
}
T query(int s, int e) {
T ret = 0;
for(s += n, e += n; s < e; s /= 2, e /= 2) {
if(s & 1) ret = C()(ret, t[s++]);
if(e & 1) ret = C()(ret, t[--e]);
}
return ret;
}
};
struct segtree {
int n, m;
vvi t;
void init(const vvi& ar) {
n = ar.size(), m = ar[0].size();
t.assign(2*n, vi(2*m, 0));
FOR(y, 0, n) { // push in leaf
FOR(x, 0, m) {
t[n+y][m+x] = ar[y][x];
}
}
RFOR(y, 2*n-1, 1) { // construct
RFOR(x, 2*m-1, 1) {
if(y < n) t[y][x] = t[y*2][x] + t[y*2+1][x];
if(x < m) t[y][x] = t[y][x*2] + t[y][x*2+1];
}
}
}
void modify(int y, int x, int c) {
t[y + n][x + m] = c; // leaf update
for(y += n; y > 0; y /= 2) {
for(int x2 = x + m; x2 > 0; x2 /= 2) {
if(y < n) t[y][x2] = t[y*2][x2] + t[y*2+1][x2];
if(x2 < m) t[y][x2] = t[y][x2*2] + t[y][x2*2+1];
}
}
}
int query(int sy, int sx, int ey, int ex) {
int ret = 0;
for(sy += n, ey += n; sy < ey; sy /= 2, ey /= 2) {
for(int x1 = sx + m, x2 = ex + m; x1 < x2; x1 /= 2, x2 /= 2) {
if(sy&1) {
if(x1&1) ret += t[sy][x1];
if(x2&1) ret += t[sy][x2-1];
}
if(ey&1) {
if(x1&1) ret += t[ey-1][x1];
if(x2&1) ret += t[ey-1][x2-1];
}
if(x1&1) ++x1;
if(x2&1) --x2;
}
if(sy&1) ++sy;
if(ey&1) --ey;
}
return ret;
}
};
const int ST_MAX = 1<<21, lf = ST_MAX/2;
struct segtree{
i64 t[ST_MAX], d[ST_MAX];
segtree(){ memset(t, 0, sizeof(t)), memset(d, 0, sizeof(d)); }
void build(){ RFOR(i, lf-1, 1) t[i] = t[i*2]+ t[i*2+1]; } // !! BUILD !!
void propagate(int u, int ns, int ne){
if(!d[u]) return;
if(u < lf){ // propagate to childs
d[u*2] += d[u];
d[u*2+1] += d[u];
}
t[u] += d[u] * (ne-ns); // update node
d[u] = 0;
}
void add(int s, int e, int x){ add(s, e, x, 1, 0, lf); } // [s, e)
void add(int s, int e, int x, int u, int ns, int ne){
propagate(u, ns, ne);
if(e <= ns || ne <= s) return;
if(s <= ns && ne <= e){
d[u] += x;
propagate(u, ns, ne);
return;
}
int mid = (ns+ne)/2;
add(s, e, x, u*2, ns, mid), add(s, e, x, u*2+1, mid, ne);
t[u] = t[u*2] + t[u*2+1];
}
i64 sum(int s, int e){ return sum(s, e, 1, 0, lf); } // [s, e)
i64 sum(int s, int e, int u, int ns, int ne){
propagate(u, ns, ne);
if(e <= ns || ne <= s) return 0;
if(s <= ns && ne <= e) return t[u];
int mid = (ns+ne)/2;
return sum(s, e, u*2, ns, mid) + sum(s, e, u*2+1, mid, ne);
}
};
struct node {
int x;
node *l, *r;
node(int _x = 0, node* l = 0, node* r = 0) : x(_x), l(l), r(r) { }
node* addtree(int u, int c, int ns = 0, int ne = MAXN-1) {
if(ns <= u && u <= ne) {
if(ns == ne) return new node(x + c, 0, 0);
int mid = (ns+ne)/2;
return new node(x + c, l->addtree(u, c, ns, mid), r->addtree(u, c, mid+1, ne));
}
return this;
}
int query(int s, int e, int ns = 0, int ne = MAXN-1) {
if(s <= ns && ne <= e) return x;
if(ne < s || e < ns) return 0;
int mid = (ns+ne)/2;
return l->query(s, e, ns, mid) + r->query(s, e, mid+1, ne);
}
} *root[MAXN+1];
struct pst {
i64 x[MAXN*LOGN];
int l[MAXN*LOGN], r[MAXN*LOGN], tcnt;
int base(int ns = 0, int ne = MAXN-1) { // make 0th tree
int u = tcnt++;
l[u] = u, r[u] = u;
}
int make(int idx, int c, int u, int ns = 0, int ne = MAXN-1) { // update from u-rooted
if(idx < ns || ne < idx) return u;
int v = tcnt++;
if(ns == ne) x[v] = (x[u] + c) % MOD;
else {
int m = (ns+ne)/2;
l[v] = make(idx, c, l[u], ns, m);
r[v] = make(idx, c, r[u], m+1, ne);
x[v] = (x[l[v]] + x[r[v]]) % MOD;
}
return v;
}
i64 query(int s, int e, int u, int ns = 0, int ne = MAXN-1) { // query from u-rooted
if(s <= ns && ne <= e) return x[u];
if(ne < s || e < ns) return 0;
int m = (ns+ne)/2;
return (query(s, e, l[u], ns, m) + query(s, e, r[u], m+1, ne)) % MOD;
}
};
/*
HLD with costed vertex.
usually (dfs_init, lca, decomposite, eidx, query) don't need to be changed.
just modify (segtree, init_segs), and if segtree function changed modify (update, query_to)
*/
const int MAXN = 1e5+10, LOGN = 18, INF = 0x7fffffff;
struct hld_vtx {
struct segtree {
int n;
vi t;
void init(int _n) { n = _n; t.assign(2*n, INF); }
void update(int u, int x) {
for(t[u += n] = x; u > 1; u /= 2) t[u/2] = min(t[u], t[u^1]);
}
int query(int s, int e) {
int ret = INF;
for(s += n, e += n; s < e; s /= 2, e /= 2) {
if(s&1) ret = min(ret, t[s++]);
if(e&1) ret = min(ret, t[--e]);
}
return ret;
}
};
int n, rt;
vi ssz, dep, hidx;
vvi g, par, hvy;
vector<segtree> segs;
hld_vtx(vvi& g, int rt) : g(g), rt(rt), n(g.size()), ssz(n, 0), dep(n, 0), hidx(n, -1), par(n, vi(LOGN, 0)) {
par[rt][0] = rt;
dfs_init(rt);
decomposite(rt);
init_segs();
}
void dfs_init(int u) { // initialize dfs info
ssz[u] = 1;
FOR(j, 1, LOGN) par[u][j] = par[par[u][j-1]][j-1];
for(int v : g[u]) {
if(par[u][0] == v) continue;
par[v][0] = u;
dep[v] = dep[u] + 1;
dfs_init(v);
ssz[u] += ssz[v];
}
}
int lca(int u, int v) { // consider par[root] = root
if(dep[u] < dep[v]) swap(u, v);
int dif = dep[u] - dep[v];
FOR(j, 0, LOGN) if(dif & (1<<j)) u = par[u][j];
if(u != v) {
RFOR(j, LOGN-1, 0) if(par[u][j] != par[v][j]) u = par[u][j], v = par[v][j];
u = par[u][0];
}
return u;
}
void decomposite(int rt) { // decomposite tree
queue<int> q;
q.push(rt);
while(q.size()) {
int u = q.front(); q.pop();
for(int v : g[u]) if(par[v][0] == u) q.push(v);
int p = par[u][0];
if(u != rt && ssz[u]*2 >= ssz[p]) { // extend h-path
hidx[u] = hidx[p];
hvy[hidx[u]].pb(u);
} else { // create h-path
hidx[u] = hvy.size();
hvy.pb(vi());
hvy[hidx[u]].pb(u);
}
}
}
void init_segs() { // initialize segtrees
segs.assign(hvy.size(), segtree());
FOR(i, 0, hvy.size()) segs[i].init(hvy[i].size()); // m nodes
}
int vidx(int u) { // get v's index in h-path
return dep[u] - dep[hvy[hidx[u]][0]];
}
void update(int u, int x) { // update v's cost
if(x == 0) segs[hidx[u]].update(vidx(u), INF);
else segs[hidx[u]].update(vidx(u), vidx(u));
}
int query(int v) { // root->v query
return query_to(0, v);
}
int query_to(int u, int v) { // return u->v path's query
if(hidx[u] == hidx[v]) {
int res = segs[hidx[u]].query(vidx(u), vidx(v)+1);
if(res == INF) return INF;
return hvy[hidx[u]][res];
}
int res = query_to(u, par[hvy[hidx[v]][0]][0]);
if(res != INF) return res;
res = segs[hidx[v]].query(0, vidx(v)+1);
if(res == INF) return INF;
return hvy[hidx[v]][res];
}
};
/*
HLD with costed edge.
Unlike the normal HLD, top edge of each chains are also belongs to chain.
usually (dfs_init, lca, decomposite, eidx, query) don't need to be changed.
just modify (segtree, init_segs), and if segtree function changed modify (update, query_to)
*/
const int LOGN = 18;
struct hld_edge {
struct segtree { // just edit segtree ( currently half-open interval [s, e) )
int n;
vi t;
void init(int _n) { n = _n; t.assign(2*n, 0); }
void update(int u, int x) {
for(t[u += n] = x; u > 1; u /= 2) t[u/2] = max(t[u], t[u^1]);
}
int query(int s, int e) {
int ret = 0;
for(s += n, e += n; s < e; s /= 2, e /= 2) {
if(s&1) ret = max(ret, t[s++]);
if(e&1) ret = max(ret, t[--e]);
}
return ret;
}
};
int n, rt;
vi ssz, dep, hidx;
vvi g, par, hvy;
vector<segtree> segs;
hld_edge(vvi& g, int rt) : g(g), rt(rt), n(g.size()), ssz(n, 0), dep(n, 0), hidx(n, -1), par(n, vi(LOGN, 0)) {
par[rt][0] = rt;
dfs_init(rt);
decomposite(rt);
init_segs();
}
void dfs_init(int u) { // initialize dfs info
ssz[u] = 1;
FOR(j, 1, LOGN) par[u][j] = par[par[u][j-1]][j-1];
for(int v : g[u]) {
if(par[u][0] == v) continue;
par[v][0] = u;
dep[v] = dep[u] + 1;
dfs_init(v);
ssz[u] += ssz[v];
}
}
int lca(int u, int v) { // consider par[root] = root
if(dep[u] < dep[v]) swap(u, v);
int dif = dep[u] - dep[v];
FOR(j, 0, LOGN) if(dif & (1<<j)) u = par[u][j];
if(u != v) {
RFOR(j, LOGN-1, 0) if(par[u][j] != par[v][j]) u = par[u][j], v = par[v][j];
u = par[u][0];
}
return u;
}
void decomposite(int rt) { // decomposite tree
hidx[rt] = -1;
queue<int> q;
q.push(rt);
while(q.size()) {
int u = q.front(); q.pop();
for(int v : g[u]) if(par[v][0] == u) q.push(v);
if(u != rt) {
int p = par[u][0];
if(p != rt && ssz[u]*2 >= ssz[p]) { // extend h-path (only if h-path)
hidx[u] = hidx[p];
hvy[hidx[u]].pb(u);
} else { // create h-path (l-path or root-h-path)
hidx[u] = hvy.size();
hvy.pb(vi());
hvy[hidx[u]].pb(p);
hvy[hidx[u]].pb(u);
}
}
}
}
void init_segs() { // initialize segtrees
segs.assign(hvy.size(), segtree());
FOR(i, 0, hvy.size()) segs[i].init(hvy[i].size()-1); // m vertices: m-1 edges
}
int eidx(int v) { // get u->v edge index in h-path
return dep[par[v][0]] - dep[hvy[hidx[v]][0]];
}
void update(int u, int v, int x) { // u->v edge update
if(par[u][0] == v) swap(u, v);
assert(par[v][0] == u);
segs[hidx[v]].update(eidx(v), x);
}
int query_to(int u, int v) { // return u->v path's query
if(u == v) return 0;
// modify range if segtree use closed interval [s, e]
if(hidx[u] == hidx[v]) return segs[hidx[u]].query(eidx(u)+1, eidx(v)+1); // e(u)+1 because target is edge
return max(query_to(u, hvy[hidx[v]][0]), segs[hidx[v]].query(0, eidx(v)+1)); // query tail path + recur
}
int query(int u, int v) {
int t = lca(u, v);
return max(query_to(t, u), query_to(t, v));
}
};
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
template<class key, class value, class cmp = less<key>>
using treemap = tree<key, value, less<int>, rb_tree_tag, tree_order_statistics_node_update>; // key, val, comp, implements, ๋
ธ๋ ๋ถ๋ณ ๊ท์น
template<class key, class cmp = less<key>>
using treeset = tree<key, null_type, cmp, rb_tree_tag, tree_order_statistics_node_update>;
#ifdef LOCAL_BOOKNU
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif
#define FOR(i, f, n) for(int (i) = (f); (i) < (int)(n); ++(i))
#define RFOR(i, f, n) for(int (i) = (f); (i) >= (int)(n); --(i))
#define pb push_back
#define emb emplace_back
#define fi first
#define se second
#define ENDL '\n'
#define sz(A) (int)(A).size()
#define ALL(A) A.begin(), A.end()
#define UNIQUE(c) (c).resize(unique(ALL(c)) - (c).begin())
typedef pair<int, int> ii;
typedef pair<int, ii> iii;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef vector<ii> vii;
typedef vector<vii> vvii;
typedef long long i64;
typedef unsigned long long ui64;
inline int getidx(const vi& ar, int x) { return lower_bound(ALL(ar), x) - ar.begin(); }
inline i64 GCD(i64 a, i64 b) { i64 n; if(a < b) swap(a, b); while(b != 0) { n = a % b; a = b; b = n; } return a; }
inline i64 LCM(i64 a, i64 b) { if(a == 0 || b == 0) return GCD(a, b); return a / GCD(a, b) * b; }
inline i64 CEIL(i64 n, i64 d) { return n / d + (i64)(n % d != 0); } // for positive numbers
inline i64 ROUND(i64 n, i64 d) { return n / d + (i64)((n % d) * 2 >= d); }
const i64 MOD = 1e9+7;
inline i64 POW(i64 a, i64 n) {
i64 ret;
for(ret = 1; n; a = a*a%MOD, n /= 2) { if(n%2) ret = ret*a%MOD; }
return ret;
}
template <class T> ostream& operator<<(ostream& os, vector<T> v) {
os << "[";
int cnt = 0;
for(auto vv : v) { os << vv; if(++cnt < v.size()) os << ","; }
return os << "]";
}
template <class T> ostream& operator<<(ostream& os, set<T> v) {
os << "[";
int cnt = 0;
for(auto vv : v) { os << vv; if(++cnt < v.size()) os << ","; }
return os << "]";
}
template <class L, class R> ostream& operator<<(ostream& os, pair<L, R> p) { return os << "(" << p.fi << "," << p.se << ")"; }
void debug_out() { cerr << endl; }
template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) { cerr << " " << H, debug_out(T...); }
// ........................... MAIN ........................... //
void input() { }
int solve() { return 0; }
void execute() { input(), solve(); }
int main(void) {
#ifdef LOCAL_BOOKNU
freopen("__IO/input.txt", "r", stdin);
// freopen("__IO/out.txt", "w", stdout);
#endif
cin.tie(0), ios_base::sync_with_stdio(false);
execute();
return 0;
}
const int RANGE = 256;
int n, k, ps[RANGE][RANGE][RANGE], ar[RANGE][RANGE][RANGE];
int f(int x, int y, int z) {
return (x < 0 || y < 0 || z < 0 || x >= RANGE || y >= RANGE || z >= RANGE ? 0 : ps[x][y][z]);
}
int sum(int x1, int y1, int z1, int l) {
int x2 = min(RANGE - 1, x1 + l), y2 = min(RANGE - 1, y1 + l), z2 = min(RANGE - 1, z1 + l);
--x1, --y1, --z1;
return
f(x2, y2, z2)
- f(x1, y2, z2) - f(x2, y1, z2) - f(x2, y2, z1)
+ f(x1, y1, z2) + f(x1, y2, z1) + f(x2, y1, z1)
- f(x1, y1, z1);
}
void init() {
FOR(x, 0, RANGE) {
FOR(y, 0, RANGE) {
FOR(z, 0, RANGE) {
ps[x][y][z] +=
f(x - 1, y, z) + f(x, y - 1, z) + f(x, y, z - 1)
- f(x - 1, y - 1, z) - f(x - 1, y, z - 1) - f(x, y - 1, z - 1)
+ f(x - 1, y - 1, z - 1);
}
}
}
}
Define $A[i][j] = k \text{ for } D[i][j] \text{ becomes minimum}$
If condition $2$, $3$ is satisfied, then
$A[i][j-1] \leq A[i][j] \leq A[i+1][j]$
__builtin_clz(int x); // count leading-zero
__builtin_ctz(int x); // count tailing-zero
__builtin_clzll(i64 x);
__builtin_ctzll(i64 x);
__builtin_popcount(int x); // number of 1-bits
__builtin__ffs(int x); // lsb index (1-based, x = 0 -> 0)
floor(log2(n)): 31 - __builtin_clz(n|1);
// 00111, 01011, 01101, 01110, 10011, 10101...
i64 next_perm(i64 x) {
i64 t = x|(x-1);
return (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(x) + 1))
}
class FastIO {
int fd, bufsz;
char *buf, *cur, *end;
public:
FastIO(int _fd = 0, int _bufsz = 1 << 20) : fd(_fd), bufsz(_bufsz) {
buf = cur = end = new char[bufsz];
}
~FastIO() { delete[] buf; }
bool readbuf() {
cur = buf;
end = buf + bufsz;
while(true) {
size_t res = fread(cur, sizeof(char), end - cur, stdin);
if(res == 0) break;
cur += res;
}
end = cur;
cur = buf;
return buf != end;
}
bool hasNext() {
while(true) {
if(cur == end && !readbuf()) return false;
if(isdigit(*cur) || *cur == '-') break;
++cur;
}
return true;
}
int r() {
while(true) {
if(cur == end) readbuf();
if(isdigit(*cur) || *cur == '-') break;
++cur;
}
bool sign = true;
if(*cur == '-') {
sign = false;
++cur;
}
int ret = 0;
while(true) {
if(cur == end && !readbuf()) break;
if(!isdigit(*cur)) break;
ret = ret * 10 + (*cur - '0');
++cur;
}
return sign ? ret : -ret;
}
} sc;
while(scanf("%d", &n) > 0) { // until input ends
scanf("%d: (%d)", &x, &y); // formatted input
for(int i = 0; i < x; ++i) scanf("%d", &z);
}
getline(cin, line);
vector<string> split(string& target, string regex) { // using regex
vector<string> ret;
std::regex rgx(regex);
std::sregex_token_iterator iter(target.begin(),
target.end(),
rgx,
-1);
std::sregex_token_iterator end;
for( ; iter != end; ++iter) ret.pb(*iter);
return ret;
}
vector<string> space_split(string& s) { // split by whitespace
istringstream iss(s);
vector<string> ret(istream_iterator<string>{iss}, istream_iterator<string>());
return ret;
}
std::to_string(42);
std::atoi("42");
s.find_by_order(x); // 0-indexed
s.order_of_key(x) // 0-indexed, find first element x <= ar[idx]
165349, 232153, 1012924417
< 10^k prime # of prime < 10^k prime
-------------------------------------------------------------
1 7 4 10 9999999967
2 97 25 11 99999999977
3 997 168 12 999999999989
4 9973 1229 13 9999999999971
5 99991 9592 14 99999999999973
6 999983 78498 15 999999999999989
7 9999991 664579 16 9999999999999937
8 99999989 5761455 17 99999999999999997
9 999999937 50847534 18 999999999999999989
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
mt19937 rng(0x14004); // 0x94949
int randint(int s, int e) { return uniform_int_distribution<int>(s, e)(rng); }
struct chash {
const int RANDOM = (long long)(make_unique<char>().get()) ^ chrono::high_resolution_clock::now().time_since_epoch().count();
static unsigned long long hash_f(unsigned long long x) {
x += 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return x ^ (x >> 31);
}
static unsigned hash_combine(unsigned a, unsigned b) { return a * 31 + b; }
int operator()(int x) const { return hash_f(x)^RANDOM; }
};
gp_hash_table<key, int, chash> mp;
int main() {
mp[1] = 1;
}
$\vec{a}\times\vec{b} = (a_2b_3-a_3b_2,\ a_3b_1-a_1b_3,\ a_1b_2-a_2b_1)$
$(\vec{i}, \vec{j}, \vec{k}$ ๋ ๊ฐ ์ถ์ ๋จ์ ๋ฒกํฐ$)$
$\vec{a} \cdot \vec{b} = \lvert\vec{a}\rvert\lvert\vec{b}\rvert\cos\theta = a_1b_1+a_2b_2$
DP
์์ ํ์
ํ์ฌ ์ซ์์์ ๊ฐ ์๋ฆฌ์๋ฅผ ์ง์๋๊ฐ ๋, ์ซ์๊ฐ ์์๋ฅผ ์ ์งํ๋ฉฐ ์ง์์ง๋ ์ต๋ ํ์๋ฅผ ์ ์๋ก ๋ณผ ๋, ๋ ์ซ์์ ์ ์๋ฅผ ๋น๊ตํ๋ ๋ฌธ์ ์ด๋ค.
๋ฌธ์ ์์ฒด๊ฐ ๊ฒ์ผ๋ก ๋ณด๊ธฐ์๋ ์๋นํ ๋ณต์กํด๋ณด์ธ๋ค.
๋จ์ํ๊ฒ ์๊ฐํ๋ฉด ๊ฐ ์๋ฆฌ๋ฅผ ์ง์๋ณด๋ฉฐ ์์๊ฐ ์ ์ง๋๋ ์ต๋๊ฐ์ ์ฐพ์๊ฐ๋ ์์ ํ์์ผ๋ก ํ ์ ์๋ค.
๋ค์ฏ ์๋ฆฌ๋ฐ์ ์ ๋๋ ์ซ์์ด๊ธฐ ๋๋ฌธ์ ์์ ํ์์ผ๋ก๋ ์๊ฐ์ด๊ณผ๊ฐ ๋์ง ์๊ฒ ์ง๋ง, DP๋ฅผ ์ฐ๋ฉด ๋ ์์ ์ ์ธ ์๊ฐ๋ณต์ก๋๋ก ๋ต์ ๊ตฌํ ์ ์์ ๊ฒ์ด๋ค.
๊ฐ ์ซ์๋ฅผ ์ํ๊ณต๊ฐ์ผ๋ก ๋ณด๊ณ ํด๋น ์ซ์์์ ๊ฐ ์๋ฆฌ์๋ฅผ ์ง์๋๊ฐ๋ฉฐ ์ฌ๊ท๋ฅผ ๋๋ฉด ๋ต์ ๊ตฌํ ์ ์๋ค.
์ซ์์ ๊ฐ ์๋ฆฌ๋ฅผ ์ง์ฐ๋๊ฒ ์ข ๊ท์ฐฎ๊ธด ํ์ง๋ง, ์ด๊ฒ์ ๋ค์ํ ๋ฐฉ๋ฒ์ผ๋ก ๊ตฌํํ ์ ์๋ค.
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL_BOOKNU
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif
// ........................macro.......................... //
#define FOR(i, f, n) for(int (i) = (f); (i) < (int)(n); ++(i))
#define RFOR(i, f, n) for(int (i) = (f); (i) >= (int)(n); --(i))
#define pb push_back
#define emb emplace_back
#define fi first
#define se second
#define ENDL '\n'
#define sz(A) (int)(A).size()
#define ALL(A) A.begin(), A.end()
#define UNIQUE(c) (c).resize(unique(ALL(c)) - (c).begin())
#define next next9876
#define prev prev1234
typedef pair<int, int> ii;
typedef pair<int, ii> iii;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef vector<ii> vii;
typedef vector<vii> vvii;
typedef long long i64;
typedef unsigned long long ui64;
// inline i64 GCD(i64 a, i64 b) { if(b == 0) return a; return GCD(b, a % b); }
inline int getidx(const vi& ar, int x) { return lower_bound(ALL(ar), x) - ar.begin(); } // ์ขํ ์์ถ์ ์ฌ์ฉ: ์ ๋ ฌ๋ ar์์ x์ idx๋ฅผ ์ฐพ์
inline i64 GCD(i64 a, i64 b) { i64 n; if(a < b) swap(a, b); while(b != 0) { n = a % b; a = b; b = n; } return a; }
inline i64 LCM(i64 a, i64 b) { if(a == 0 || b == 0) return GCD(a, b); return a / GCD(a, b) * b; }
inline i64 CEIL(i64 n, i64 d) { return n / d + (i64)(n % d != 0); } // ์์์ผ ๋ ์ด์ํ๊ฒ ์๋ํ ์ ์์.
inline i64 ROUND(i64 n, i64 d) { return n / d + (i64)((n % d) * 2 >= d); }
const i64 MOD = 1e9+7;
inline i64 POW(i64 a, i64 n) {
assert(0 <= n);
i64 ret;
for(ret = 1; n; a = a*a%MOD, n /= 2) { if(n%2) ret = ret*a%MOD; }
return ret;
}
template <class T> ostream& operator<<(ostream& os, vector<T> v) {
os << "[";
int cnt = 0;
for(auto vv : v) { os << vv; if(++cnt < v.size()) os << ","; }
return os << "]";
}
template <class T> ostream& operator<<(ostream& os, set<T> v) {
os << "[";
int cnt = 0;
for(auto vv : v) { os << vv; if(++cnt < v.size()) os << ","; }
return os << "]";
}
template <class L, class R> ostream& operator<<(ostream& os, pair<L, R> p) { return os << "(" << p.fi << "," << p.se << ")"; }
void debug_out() { cerr << endl; }
template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) { cerr << " " << H, debug_out(T...); }
// ....................................................... //
const int MAXN = 3e4+10;
int a, b, dp[MAXN];
// --> x*pr[j]์์ overflow ๋ฐ์ํ ์ ์์! ๊ทธ๋ฌ๋ ์ ๋นํ ์์ RANGE์์๋ ์์ ๋ถํฌ๊ฐ ์ด์ดํ๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ์ง ์์.
// --> RANGE ๋ด์ ์์ ๊ฐ์ = RANGE/ln(RANGE) --> 2e7์์ ์ธ์ ์์ ์ต๋ ์ฐจ์ด = 180, 4e6 = 140
const int RANGE = MAXN;
int pn, spf[RANGE], pr[RANGE]; // pr์ vi๋ก ๊ตฌํํด๋ ๋จ. --> but ์คํ ์๊ฐ ๋ง์ด ๋์ด๋จ.
void eulerSieve() {
FOR(x, 2, RANGE) {
if(!spf[x]) spf[x] = pr[pn++] = x; // x ์์ฒด๊ฐ ์์์ด๋ฉด spf[x] = x์ด๋ค.
for(int j = 0; x*pr[j] < RANGE; ++j) { // x์ ์์๋ฐฐ๋ค์ spf[x*pr] = pr๋ก ์น ํด์ค๋ค. (๋จ, pr <= spf[x])
spf[x*pr[j]] = pr[j];
if(x % pr[j] == 0) break; // ์ข ๋ ์ง๊ด์ ์ผ๋ก if(spf[x] == pr[j])๋ก ๊ตฌํํด๋ ๋์ง๋ง, ๋๊ฐ์ ํจ๊ณผ๋ฅผ ๊ฐ์ง ์ฝ๋์ด๋ค.
}
}
}
void input() {
// ---- !!! INIT GLOBAL VARIABLES !!! ---- //
// ---- Interactive Cautions : cin.tie(0), freopen(...), Case #x: ---- //
cin >> a >> b;
}
int f(int x) {
int& ret = dp[x];
if(ret != -1) return ret;
if(spf[x] != x) return ret = 0; // ์์๊ฐ ์๋
int cur = x, w = 1;
ret = 0;
while(cur) { // ๊ฐ ์๋ฆฌ๋ฅผ ์ง์๋ณด๋ฉฐ ์ฌ๊ท
ret = max(ret, f((cur/10) * w + x % w));
cur /= 10;
w *= 10;
}
return ++ret;
}
int solve() {
if(f(a) > f(b)) cout << 1 << ENDL;
else if(f(a) < f(b)) cout << 2 << ENDL;
else cout << 3 << ENDL;
return 0;
}
// ................. main .................. //
void execute() {
memset(dp, -1, sizeof(dp));
eulerSieve();
spf[0] = -1;
int TTTT; cin >> TTTT;
FOR(tttt, 0, TTTT) {
cout << "Case #" << tttt+1 << ENDL;
input(), solve();
}
}
int main(void) {
#ifdef LOCAL_BOOKNU
// freopen("input.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
#endif
cin.tie(0), ios_base::sync_with_stdio(false);
execute();
return 0;
}
// ......................................... //
DP
ํฐ๋ฆฐ๋๋กฌ
๋ถ๋ถ ํฉ
๊ธธ์ด๊ฐ ๊ฐ์ $A, B$ ์์ด์ด ์์ ๋, ๊ฐ์ ์ธ๋ฑ์ค์ ๊ฐ์ ์๊ฐ ์๋ ์๋ฅผ ์ ์ฌ๋๋ผ๊ณ ํ๋ค.
$B$์์ด์ ์ผ๋ถ๋ฅผ ํ ๋ฒ๋ง ๋ค์ง์ ์ ์์ ๋, $A, B$ ์์ด์ ์ ์ฌ๋์ ์ต๋๊ฐ์ ๊ตฌํด์ผํ๋ค.
๊ตฌํ์ด ์๊ทผํ ๊น๋ค๋ก์ด ๋ฌธ์ ์ด๋ค. ์ฐ์ , ๊ฐ์ฅ ์ฌ์ด ์์ด๋์ด๋ก $B$์ ๋ชจ๋ ๊ตฌ๊ฐ์ ๋ค์ง์ด๋ณด๊ณ ์ ์ฌ๋๋ฅผ ํ๋จํ๋ ๋ฐฉ๋ฒ์ด ์๋ค.
ํ์ง๋ง ์ด ๋ฐฉ๋ฒ์ ๋ชจ๋ ๊ตฌ๊ฐ์ ์ ํํ๋๋ฐ $n^2$, ์ ์ฌ๋๋ฅผ ๋น๊ตํ๋๋ฐ $n$๋ฒ์ ์ฐ์ฐ์ด ํ์ํ๊ธฐ ๋๋ฌธ์ $O(n^3)$์ ์๊ฐ๋ณต์ก๋๋ฅผ ๊ฐ๊ณ , ์๊ฐ์ด๊ณผ๊ฐ ๋ฐ์ํ๋ค.
$B$์ $[s, e]$ ๊ตฌ๊ฐ์ ๋ค์ง์์ ๋๋ฅผ ์๊ฐํด๋ณด๋ฉด, ๊ตฌ๊ฐ์ ๊ธธ์ด๊ฐ ํ์์ธ ๊ฒฝ์ฐ ์ด๋ค ์ค์ ์ ๊ธฐ์ค์ผ๋ก A์ ๋ค์ง์ด์ง ์ํ๋ก ๋งคํ๋๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
์ฆ, ์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๋งคํ๋๋ค.
์ด๊ฒ์ ์ด์ฉํ๋ฉด ํฐ๋ฆฐ๋๋กฌ์ ๋ฌธ์ ์ฒ๋ผ ๊ตฌ๊ฐ์ ๊ธธ์ด๋ฅผ ์ ์ฐจ ๋๋ ค๊ฐ๋ ๋ฐฉ์์ ์ฌ์ฉํด ๋ค์งํ ๊ตฌ๊ฐ์ ๋ํ ์ ์ฌ๋ ๋น๊ต๋ฅผ O(1)์ ํ ์ ์๋ค.
(์ด ๋, ๊ตฌํ์ ๋ ์ฝ๊ฒ ํ๊ธฐ ์ํด์ ๋ฌธ์์ด ๋ฌธ์ ์์ ๋ง์ด ์ฌ์ฉํ๋ ์ซ์ ์ฌ์ด์ #์ ๋ฃ๋ ํธ๋ฆญ์ ์ฌ์ฉํ ์ ์๋ค.)
๋ํ ๋ค์งํ์ง ์์ ๊ตฌ๊ฐ์ ๋ํด์๋ partial sum
์์ด๋์ด๋ฅผ ์ด์ฉํ์ฌ $[0, i]$์ ์ ์ฌ๋๋ฅผ ๋ฐฐ์ด ํํ๋ก ์ ์ฅํด๋๋ฉด ์์์ ๊ตฌ๊ฐ์ ๋ํ ์ ์ฌ๋ ์ญ์ $O(1)$์ ๊ตฌํ ์ ์์ผ๋ฉฐ, ๊ฒฐ๊ณผ์ ์ผ๋ก ๋ชจ๋ ๊ตฌ๊ฐ์ ๋ํด์ ์ ์ฌ๋ ๋น๊ต๋ฅผ $O(1)$์ ํ ์ ์์ผ๋ฏ๋ก ์๊ฐ ๋ณต์ก๋๋ $O(n^2)$์ด ๋๋ค.
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL_BOOKNU
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif
// ........................macro.......................... //
#define FOR(i, f, n) for(int (i) = (f); (i) < (int)(n); ++(i))
#define RFOR(i, f, n) for(int (i) = (f); (i) >= (int)(n); --(i))
#define pb push_back
#define emb emplace_back
#define fi first
#define se second
#define ENDL '\n'
#define sz(A) (int)(A).size()
#define ALL(A) A.begin(), A.end()
#define UNIQUE(c) (c).resize(unique(ALL(c)) - (c).begin())
#define next next9876
#define prev prev1234
typedef pair<int, int> ii;
typedef pair<int, ii> iii;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef vector<ii> vii;
typedef vector<vii> vvii;
typedef long long i64;
typedef unsigned long long ui64;
// inline i64 GCD(i64 a, i64 b) { if(b == 0) return a; return GCD(b, a % b); }
inline int getidx(const vi& ar, int x) { return lower_bound(ALL(ar), x) - ar.begin(); } // ์ขํ ์์ถ์ ์ฌ์ฉ: ์ ๋ ฌ๋ ar์์ x์ idx๋ฅผ ์ฐพ์
inline i64 GCD(i64 a, i64 b) { i64 n; if(a < b) swap(a, b); while(b != 0) { n = a % b; a = b; b = n; } return a; }
inline i64 LCM(i64 a, i64 b) { if(a == 0 || b == 0) return GCD(a, b); return a / GCD(a, b) * b; }
inline i64 CEIL(i64 n, i64 d) { return n / d + (i64)(n % d != 0); } // ์์์ผ ๋ ์ด์ํ๊ฒ ์๋ํ ์ ์์.
inline i64 ROUND(i64 n, i64 d) { return n / d + (i64)((n % d) * 2 >= d); }
const i64 MOD = 1e9+7;
inline i64 POW(i64 a, i64 n) {
assert(0 <= n);
i64 ret;
for(ret = 1; n; a = a*a%MOD, n /= 2) { if(n%2) ret = ret*a%MOD; }
return ret;
}
template <class T> ostream& operator<<(ostream& os, vector<T> v) {
os << "[";
int cnt = 0;
for(auto vv : v) { os << vv; if(++cnt < v.size()) os << ","; }
return os << "]";
}
template <class T> ostream& operator<<(ostream& os, set<T> v) {
os << "[";
int cnt = 0;
for(auto vv : v) { os << vv; if(++cnt < v.size()) os << ","; }
return os << "]";
}
template <class L, class R> ostream& operator<<(ostream& os, pair<L, R> p) { return os << "(" << p.fi << "," << p.se << ")"; }
void debug_out() { cerr << endl; }
template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) { cerr << " " << H, debug_out(T...); }
// ....................................................... //
const int MAXN = 5e3+1;
int n, ar[MAXN], br[MAXN], ps[MAXN], ev[MAXN], od[MAXN];
void input() {
// ---- !!! INIT GLOBAL VARIABLES !!! ---- //
cin >> n;
FOR(i, 0, n) cin >> ar[i];
FOR(i, 0, n) cin >> br[i];
memset(ps, 0, sizeof(ps));
memset(ev, 0, sizeof(ev));
}
int rs(int s, int e) {
if(s > e) return 0;
return (e < n ? ps[e] : ps[n-1]) - (s > 0 ? ps[s-1] : 0);
}
int solve() {
FOR(i, 0, n) ps[i] = (i ? ps[i-1] : 0) + (ar[i] == br[i]);
int ans = ps[n-1];
// ํ์
FOR(i, 0, n) od[i] = ar[i] == br[i];
FOR(len, 1, n) {
FOR(i, len, n-len) {
if(ar[i-len] == br[i+len]) ++od[i];
if(ar[i+len] == br[i-len]) ++od[i];
ans = max(ans, od[i] + rs(0, i-len-1) + rs(i+len+1, n-1));
}
FOR(i, len-1, n-len) {
if(ar[i+len] == br[i-len+1]) ++ev[i];
if(ar[i-len+1] == br[i+len]) ++ev[i];
ans = max(ans, ev[i] + rs(0, i-len) + rs(i+len+1, n-1));
}
}
cout << ans << ENDL;
return 0;
}
// ................. main .................. //
void execute() {
#ifdef LOCAL_BOOKNU
auto START_TIME = clock();
#endif
int TTT; cin >> TTT;
FOR(ttt, 0, TTT) cout << "Case #" << ttt+1 << ENDL,
input(), solve();
#ifdef LOCAL_BOOKNU
auto END_TIME = clock();
cout << ENDL << END_TIME - START_TIME << "ms" << ENDL;
#endif
}
int main(void) {
#ifdef LOCAL_BOOKNU
// freopen("input.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
#endif
cin.tie(0), ios_base::sync_with_stdio(false);
execute();
return 0;
}
// ......................................... //
๊ทธ๋ฆฌ๋
๊ธฐํ
์ฒ์ฅ๊ณผ ๋ฐ๋ฅ์ผ๋ก ์ด๋ฃจ์ด์ง ๊ตฌ๋ถ๊ตฌ๋ถํ ๋ํ์ด ์ฃผ์ด์ง ๋, ์ข์ธก ๋ $h_s$ ๋์ด์์ ์ถ๋ฐํ์ฌ ์ฐ์ธก ๋ $h_e$ ๋์ด๋ก ์ต๋จ๊ฑฐ๋ฆฌ๋ก ์ด๋ํ ๋, ์ฌ๋ฌ ์ต๋จ ๊ฒฝ๋ก๋ค์ด ๋์ฌ ๊ฒ์ด๋ค.
(๋ํ์ ๋ณ์ $x$์ถ ํน์ $y$์ถ๊ณผ ํํํ๋ฉฐ, ์ด๋ ์ญ์ ์ํ์ผ๋ก๋ง ํ๋ค.)
์ด ๋ ๊ฐ๋ฅํ ์ต๋จ ๊ฒฝ๋ก๋ค์ ํ๊บผ๋ฒ์ ๊ทธ๋ฆฌ๋ฉด ์ด๋ค ๋ฉด์ ์ด ๋์ฌํ ๋ฐ ๊ทธ ๋ฉด์ ์ ๋๋น๋ฅผ ๊ตฌํ๋ฉด ๋๋ค.
์ฐ์ , ์ด์ํ๊ฒ ์๊ธด ๋ํ์ ์ต์ํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋๋ก ์ ์ฒ๋ฆฌ๋ฅผ ํด๋ณด์.
๋ํ์ ์์ง ๋จ์๋ก ๋์ผ๋ฉด ์ฌ๋ฌ ์ง์ฌ๊ฐํ๋ค์ด ์ํ์ผ๋ก ๋์ด๋ ํํ๊ฐ ๋๋ค.
(์ฆ, ์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ๋๋ค.)
ํจ์ฌ ์ฒ๋ฆฌํ๊ธฐ๊ฐ ํธํ๊ฒ ๋ณํ๋ค.
๋ํ์ด ์์ ๋, ๋ค์๊ณผ ๊ฐ์ ๋ฉด์ ์ ๊ตฌํ๋ ๊ฒ์ด ๋ฌธ์ ์ ์๊ตฌ์ฌํญ์ด๋ค.
์ด ๋ ๊ด์ฐฐํ ์ ์๋ ๊ฒ์, ์ด ๋ฉด์ ์ด ์ด๋ ํ ์ ๊ป์ง๊ณผ, ์๋ ๊ป์ง๋ก ์ด๋ฃจ์ด์ง ๋ํ์ด๋ผ๋ ๊ฒ์ด๋ค.
๊ทธ ๊ป์ง์ด ๊ฐ์ง๋ ํน์ฑ์ ์์๋ด๋ฉด, ์ฝ๊ฒ ๋ฉด์ ๋ ์์๋ผ ์ ์์ ๊ฒ์ด๋ค.
์ฐ์ ์๋ ๊ป์ง์ ์ ์ณ๋๊ณ , ์ ๊ป์ง์ ํน์ฑ๋ถํฐ ์์๋ณด์.
์ง๊ด์ ์ผ๋ก๋ ์ ๊ป์ง์ ํน์ฑ์ด ๋ํ์ ์์๋๋ก ๋ค์ง์ ๋ํ์ ํน์ฑ์ ์ ์ฉ๋๋ฉด ์๋ ๊ป์ง์ ๊ตฌํ ์ ์์ ๊ฒ ๊ฐ๋ค๋ ์ถ์ธก์ ํ ์ ์์ผ๋ฏ๋ก, ์ ๊ป์ง๋ถํฐ ์์๋ณด์.
๋ํ, ๋ค์ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด ์ผ๊ด์ฑ์ ์ํด ์์/๋ ๋์ด์ ์๊ด ์์ด, ๊ฐ์ฅ ์ผ์ชฝ ๋ํ์ ๋งจ ์์ชฝ์์ ์์ํด ๊ฐ์ฅ ์ค๋ฅธ์ชฝ ๋ํ์ ๋งจ ์์ชฝ์ผ๋ก ๊ฐ๋ ๊ฒฝ๋ก๋ค์ ๋ํด์ ์๊ฐํ์.
์ ๊ป์ง์ ๊ฐ๋ฅํ ์ต๋จ ๊ฒฝ๋ก ์ค ์ต๋ํ ์์ชฝ์ ํ๊ณ ๊ฐ๋ ค๋ ํน์ฑ์ ๊ฐ์ง๊ณ ์๋ค.
ํ์ง๋ง, ๋ ๋์ด๊ฐ ๋ฎ์ ๊ฒฝ์ฐ๋ ๋ง๋ฅ ์์ชฝ์ ํ๊ณ ๊ฐ ์๋ ์๋ค.
๊ทธ๋ ๋ค๊ณ ํญ์ ๋ ๋์ด๋ณด๋ค ๋ฎ์ ๋์ด๋ฅผ ์ ์งํด์ผ ํ๋ ๊ฒ๋ ์๋๋ค.
๋ฐ๋ผ์ ์ด๋ค ์ฌ์ด์ ๊ด๊ณ๋ฅผ ์ฐพ๊ธฐ ์ํด ํ์ฌ ์ด๋ค ์ง์ฌ๊ฐํ $i$๊น์ง ์๊ณ , ํ์ฌ ๋์ด๊ฐ $h$์ด๋ฉฐ, ํ์ฌ ์ง์ฌ๊ฐํ์ ๋ค์ ์ง์ฌ๊ฐํ ์ค ์์์ ์ง์ฌ๊ฐํ์ $j$๋ผ๊ณ ํ ๋, ์ด ์ง์ฌ๊ฐํ์์๋ ์ด๋ค ๋์ด๋ฅผ ์ ์งํด์ผ ํ๋์ง๋ฅผ ์์๋ณด์.
๋จ์ํ ๋ฐ๋ก ๋ค์ ์ง์ฌ๊ฐํ๋ง์ ๋ณด๊ณ ๊ฒฐ์ ํ ์ ์๋ค๋ ๊ฒ์ ๊ฐ๋จํ ์์ ๋ค๋ก ์ ์ ์๋ค.
๊ทธ๋ฌ๋ฏ๋ก ๋ค์ ๋ชจ๋ ์ง์ฌ๊ฐํ์ ๊ณ ๋ คํด์ผ ํ๋๋ฐ, ์ฌ๋ฌ๊ฐ์ง ์๋๋ฅผ ํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ ๊ฐ์ง ๊ฒฝ์ฐ๊ฐ ๋์ด๋ฅผ ์ ํํ๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
(์ค๋ช ์ ํธ์๋ฅผ ์ํด ์ง์ฌ๊ฐํ์ ์๋ ๋์ด๋ฅผ $h_s$, ์ ๋์ด๋ฅผ $h_e$๋ผ๊ณ ํ๋ค.)
๋์ด๋ฅผ ๋์ผ ์ ์๋ ๊ฒฝ์ฐ (์ํ)
$h[i]_e > h[j]_e$ ์ธ $j$๊ฐ ์กด์ฌํ ๊ฒฝ์ฐ, ํ์ฌ ์ง์ฌ๊ฐํ์์๋ ์ต๋ $h[j]_e$๊น์ง ๋ฐ์ ๋ชป์ฌ๋ผ๊ฐ๋ค.
๋์ด๋ฅผ ์ค์ผ ์ ์๋ ๊ฒฝ์ฐ (ํํ)
1๋ฒ๊ณผ ๋ฐ๋๋ก ๋์ด๋ฅผ $x$๋ก ์ค์ด๋ ค๊ณ ํ ๋, $x < h[j]_s$ ์ธ $j$๊ฐ ์กด์ฌํ ๊ฒฝ์ฐ, ๋์ด๋ $h[j]_s$๊น์ง ๋ฐ์ ์ค์ด์ง ๋ชปํ๋ค.
์ ๊ป์ง์ด ์ต๋ํ ์๋ฅผ ํ๊ณ ๊ฐ๋ ค๋ ํน์ฑ์ด ์๋ค๋ฉด, ์๋ ๊ป์ง ์ญ์ ์ต๋ํ ์๋๋ฅผ ๋ค๊ณ ๊ฐ๋ ค๋ ํน์ฑ์ด ์๋ค.
๋ฐ๋ผ์ ์ ๊ป์ง์ ๊ตฌํ๋ ์๊ณ ๋ฆฌ์ฆ์ ๊ทธ๋๋ก ์ด์ฉํ๋ฉด ์๋ ๊ป์ง ์ญ์ ์ฝ๊ฒ ๊ตฌํ ์ ์๋ค.
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL_BOOKNU
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif
// ........................macro.......................... //
#define FOR(i, f, n) for(int (i) = (f); (i) < (int)(n); ++(i))
#define RFOR(i, f, n) for(int (i) = (f); (i) >= (int)(n); --(i))
#define pb push_back
#define emb emplace_back
#define fi first
#define se second
#define ENDL '\n'
#define sz(A) (int)(A).size()
#define ALL(A) A.begin(), A.end()
#define UNIQUE(c) (c).resize(unique(ALL(c)) - (c).begin())
#define next next9876
#define prev prev1234
typedef pair<int, int> ii;
typedef pair<int, ii> iii;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef vector<ii> vii;
typedef vector<vii> vvii;
typedef long long i64;
typedef unsigned long long ui64;
// inline i64 GCD(i64 a, i64 b) { if(b == 0) return a; return GCD(b, a % b); }
inline int getidx(const vi& ar, int x) { return lower_bound(ALL(ar), x) - ar.begin(); } // ์ขํ ์์ถ์ ์ฌ์ฉ: ์ ๋ ฌ๋ ar์์ x์ idx๋ฅผ ์ฐพ์
inline i64 GCD(i64 a, i64 b) { i64 n; if(a < b) swap(a, b); while(b != 0) { n = a % b; a = b; b = n; } return a; }
inline i64 LCM(i64 a, i64 b) { if(a == 0 || b == 0) return GCD(a, b); return a / GCD(a, b) * b; }
inline i64 CEIL(i64 n, i64 d) { return n / d + (i64)(n % d != 0); } // ์์์ผ ๋ ์ด์ํ๊ฒ ์๋ํ ์ ์์.
inline i64 ROUND(i64 n, i64 d) { return n / d + (i64)((n % d) * 2 >= d); }
const i64 MOD = 1e9+7;
inline i64 POW(i64 a, i64 n) {
assert(0 <= n);
i64 ret;
for(ret = 1; n; a = a*a%MOD, n /= 2) { if(n%2) ret = ret*a%MOD; }
return ret;
}
template <class T> ostream& operator<<(ostream& os, vector<T> v) {
os << "[";
int cnt = 0;
for(auto vv : v) { os << vv; if(++cnt < v.size()) os << ","; }
return os << "]";
}
template <class T> ostream& operator<<(ostream& os, set<T> v) {
os << "[";
int cnt = 0;
for(auto vv : v) { os << vv; if(++cnt < v.size()) os << ","; }
return os << "]";
}
template <class L, class R> ostream& operator<<(ostream& os, pair<L, R> p) { return os << "(" << p.fi << "," << p.se << ")"; }
void debug_out() { cerr << endl; }
template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) { cerr << " " << H, debug_out(T...); }
// ....................................................... //
struct rect {
int s, e, w;
rect(int s = 0, int e = 0, int w = 0) : s(s), e(e), w(w) { }
};
const int MAXN = 1e5+10;
int n, m, tlen, begh, finh, ah[MAXN], aw[MAXN], bh[MAXN], bw[MAXN];
vector<rect> ar;
void input() {
// ---- !!! INIT GLOBAL VARIABLES !!! ---- //
cin >> tlen >> begh >> finh;
cin >> n;
FOR(i, 0, n) cin >> aw[i] >> ah[i]; ah[n] = ah[n-1];
cin >> m;
FOR(i, 0, m) cin >> bw[i] >> bh[i]; bh[m] = bh[m-1];
}
// ์ง์ฌ๊ฐํ ํํ๋ก ๋ง๋ ๋ค.
void generateRec() {
ar.clear();
int ap = 0, bp = 0, x = 0, ax = 0, bx = 0;
while(ap < n || bp < m) {
rect cur;
if(bp == m || (ap < n && ax + aw[ap] < bx + bw[bp])) {
cur = { bh[bp], ah[ap], ax + aw[ap] - x };
x = ax = ax + aw[ap];
++ap;
} else {
cur = { bh[bp], ah[ap], bx + bw[bp] - x };
x = bx = bx + bw[bp];
++bp;
}
if(cur.w != 0) ar.pb(cur);
}
}
// ์กฐ๊ฑด์ ๋ง๋ ๊ป์ง์ { height }๋ก ๋ฐํ
vector<int> getConvex(vector<rect>& ar, int beg) {
vector<int> ret(ar.size(), 0);
vector<int> minu(ar.size()), maxd(ar.size());
// i์์๋ถํฐ์ ์ํ, ํํ์ ๊ตฌํ๋ค.
RFOR(i, sz(ar)-1, 0) {
minu[i] = min(i+1 == ar.size() ? 0x7fffffff : minu[i+1], ar[i].e);
maxd[i] = max(i+1 == ar.size() ? -0x7fffffff : maxd[i+1], ar[i].s);
// ํ์ฌ์์ ์ํ, ํํ์ด ์๋ก๋ฅผ ๋ฐ์ด ๋๋ ๊ฒฝ์ฐ
// ์ํ์ด ํํ๋ณด๋ค ๋ฎ์์ง๋ค๋ฉด? ํํ์ ์ด๊ฑธ๋ก ์กฐ์ !
if(ar[i].e < maxd[i]) maxd[i] = ar[i].e;
// ํํ์ด ์ํ๋ณด๋ค ๋์์ง๋ค๋ฉด? ์ํ์ ์ด๊ฑธ๋ก ์กฐ์ !
if(ar[i].s > minu[i]) minu[i] = ar[i].s;
}
int cur = beg;
FOR(i, 0, ar.size()) {
// ํ์ฌ ๋์ด๋ณด๋ค ๋ฎ์์ง ํ์๋ ์๋ค.
ret[i] = min(cur, ar[i].e);
// ํํ๋ณด๋ค ๋ฎ์์ง ํ์๋ ์๋ค.
ret[i] = max(ret[i], min(ar[i].e, maxd[i]));
// ์ํ๋ณด๋ค๋ ๋ฎ์์ผ ํ๋ค.
ret[i] = max(ret[i], max(ar[i].s, minu[i]));
cur = ret[i];
}
return ret;
}
int solve() {
generateRec();
// ๊ท์ฐฎ์๊ฑฐ ๋ฐ๋ก ์ฒ๋ฆฌ
if(ar.size() == 1) {
cout << (i64)ar[0].w * abs(begh - finh) << ENDL;
return 0;
}
// ์ ๊ป์ง ์ฐพ๊ธฐ
vector<rect> tmp = ar;
tmp.pb({ finh, finh, 0 });
vector<int> up = getConvex(tmp, begh);
debug(up);
// ์๋ ๊ป์ง ์ฐพ๊ธฐ
FOR(i, 0, ar.size()) {
ar[i].s = -ar[i].s;
ar[i].e = -ar[i].e;
swap(ar[i].s, ar[i].e);
}
ar.pb({ -finh, -finh, 0 });
vector<int> dw = getConvex(ar, -begh);
debug(dw);
// ๊ณ์ฐ
i64 ans = 0;
FOR(i, 0, ar.size()-1) {
ans += (i64)ar[i].w * (up[i] + dw[i]);
}
cout << ans << ENDL;
return 0;
}
// ................. main .................. //
void execute() {
#ifdef LOCAL_BOOKNU
auto START_TIME = clock();
#endif
int TTT; cin >> TTT;
FOR(ttt, 0, TTT) cout << "Case #" << ttt+1 << ENDL,
input(), solve();
#ifdef LOCAL_BOOKNU
auto END_TIME = clock();
cout << ENDL << END_TIME - START_TIME << "ms" << ENDL;
#endif
}
int main(void) {
#ifdef LOCAL_BOOKNU
// freopen("input.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
#endif
cin.tie(0), ios_base::sync_with_stdio(false);
execute();
return 0;
}
// ......................................... //
์ฝ์งํ๋ฉด์ ํธ๋๋ผ ์ค๋ช ๋ ์ข ์ด์ํด์ง๊ณ ์ฝ๋์๋ ์ธ๋ชจ ์๋ ๋ถ๋ถ์ด ์์ ๊ฒ ๊ฐ์๋ฐ, ๋์ค์ ๋ ๊น๋ํ ํ์ด๋ฅผ ๋ณด๊ณ ๋ค์ ํ์ด๋ด์ผ๊ฒ ๋ค.
ํด๋ฆฌ์คํฑ
๊ทธ๋ฆฌ๋
$50 \times 500$์ ๋งต์ด ์ฃผ์ด์ง๊ณ , ๋ถ์ด์ผ ํ ์ง๋ค์ด ์ฃผ์ด์ง๋ค.
ํ๋์ ํญํ์ $3 \times 3$๋ฒ์๋ง์ ํญํ์ํฌ ์ ์์ ๋, ํญํ์ ์ต์๋ก ์ฌ์ฉํด ๋ชจ๋ ์ง์ ๋ถ์ด์ผ ํ๋ค.
์ฐ์ ์์ ์์ ๊ฐ๋ ์ ์ผ๋ก ํ์ด์ ์ดํดํ๋ฉด, ์ผ๋จ ์ธ๊ทธ๋จผํธ ๋จ์๋ก ์ชผ๊ฐ ํ, ๊ฐ ์ธ๊ทธ๋จผํธ์ ๋ค์ด์๋ ์์๋ค์ ํฉ์ ์ธ๊ทธ๋จผํธ idx๋ฅผ ๊ณฑํ๊ฒ๋ค์ ํฉ์ ๊ตฌํ๋ ๊ฒ์ผ๋ก ๋ฐ๋๋ค. ์ฆ, ํ์ฌ ๋งต์์ $3 \times 3$ ๋ฒ์ ์ค ์ง์ด ๊ฐ์ฅ ๋ง์ด ํฌํจ๋๋ ๊ณณ์ ์ ํํด ํญํ์ ์ฌ์ฉํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ๋ค.
๋ชจ๋ ๊ฒฝ์ฐ์์ ์ต์ ์ ๊ฒฐ๊ณผ๊ฐ ๋์ค์ง๋ ์์ง๋ง, ๊ทธ๋ญ์ ๋ญ ์ข์ ๊ฒฐ๊ณผ๋ ๋์จ๋ค.
๋จ, ํ์ฌ ๋งต ์ํ์์ ๋ชจ๋ ๊ณณ์ ํญํ์ ํฐ๋จ๋ ค๋ณด๋ ๊ฒ์ ์๊ฐ์ด๊ณผ๊ฐ ๋ฐ์ํ ์ ์์ผ๋ฏ๋ก, ์ฝ๊ฐ ํธ๋ฆฌํคํ๊ฒ ๊ตฌํํ๋ค.
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL_BOOKNU
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]:", debug_out(__VA_ARGS__)
#else
#define debug(...) 42
#endif
// ........................macro.......................... //
#define FOR(i, f, n) for(int (i) = (f); (i) < (int)(n); ++(i))
#define RFOR(i, f, n) for(int (i) = (f); (i) >= (int)(n); --(i))
#define pb push_back
#define emb emplace_back
#define fi first
#define se second
#define ENDL '\n'
#define sz(A) (int)(A).size()
#define ALL(A) A.begin(), A.end()
#define UNIQUE(c) (c).resize(unique(ALL(c)) - (c).begin())
#define next next9876
#define prev prev1234
typedef pair<int, int> ii;
typedef pair<int, ii> iii;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef vector<ii> vii;
typedef vector<vii> vvii;
typedef long long i64;
typedef unsigned long long ui64;
// inline i64 GCD(i64 a, i64 b) { if(b == 0) return a; return GCD(b, a % b); }
inline int getidx(const vi& ar, int x) { return lower_bound(ALL(ar), x) - ar.begin(); } // ์ขํ ์์ถ์ ์ฌ์ฉ: ์ ๋ ฌ๋ ar์์ x์ idx๋ฅผ ์ฐพ์
inline i64 GCD(i64 a, i64 b) { i64 n; if(a < b) swap(a, b); while(b != 0) { n = a % b; a = b; b = n; } return a; }
inline i64 LCM(i64 a, i64 b) { if(a == 0 || b == 0) return GCD(a, b); return a / GCD(a, b) * b; }
inline i64 CEIL(i64 n, i64 d) { return n / d + (i64)(n % d != 0); } // ์์์ผ ๋ ์ด์ํ๊ฒ ์๋ํ ์ ์์.
inline i64 ROUND(i64 n, i64 d) { return n / d + (i64)((n % d) * 2 >= d); }
const i64 MOD = 1e9+7;
inline i64 POW(i64 a, i64 n) {
assert(0 <= n);
i64 ret;
for(ret = 1; n; a = a*a%MOD, n /= 2) { if(n%2) ret = ret*a%MOD; }
return ret;
}
template <class T> ostream& operator<<(ostream& os, vector<T> v) {
os << "[";
int cnt = 0;
for(auto vv : v) { os << vv; if(++cnt < v.size()) os << ","; }
return os << "]";
}
template <class T> ostream& operator<<(ostream& os, set<T> v) {
os << "[";
int cnt = 0;
for(auto vv : v) { os << vv; if(++cnt < v.size()) os << ","; }
return os << "]";
}
template <class L, class R> ostream& operator<<(ostream& os, pair<L, R> p) { return os << "(" << p.fi << "," << p.se << ")"; }
void debug_out() { cerr << endl; }
template <typename Head, typename... Tail> void debug_out(Head H, Tail... T) { cerr << " " << H, debug_out(T...); }
// ....................................................... //
const int MAXN = 510;
int n, m, dp[51][MAXN];
string g[51];
set<iii> lis;
void input() {
// ---- !!! INIT GLOBAL VARIABLES !!! ---- //
cin >> n >> m;
FOR(i, 0, n) cin >> g[i];
}
void eraseBomb(int y, int x) {
for(int dy : {-1, 0, 1}) {
for(int dx : {-1, 0, 1}) {
int ny = y+dy, nx = x+dx;
if(0 < ny && ny < n-1 && 0 < nx && nx < m-1) {
auto it = lis.find({ dp[ny][nx],{ ny, nx } });
if(it != lis.end()) {
dp[ny][nx] = it->first + 1;
lis.erase(it);
lis.insert({ dp[ny][nx], {ny, nx} });
}
}
}
}
}
int solve() {
lis.clear();
memset(dp, 0, sizeof(dp));
FOR(i, 1, n-1) {
FOR(j, 1, m-1) {
int cc = 0;
for(int dy : { -1, 0, 1 }) {
for(int dx : { -1, 0, 1 }) {
int ny = i + dy, nx = j + dx;
if(g[ny][nx] == '1') {
++cc;
}
}
}
lis.insert({ -cc, { i, j } });
dp[i][j] = -cc;
}
}
int rem = 0;
FOR(i, 0, n) FOR(j, 0, m) if(g[i][j] == '1') ++rem;
vii ans;
while(rem) {
iii cur = *lis.begin();
lis.erase(lis.begin());
int cc = -cur.fi, cy = cur.se.fi, cx = cur.se.se;
rem -= cc;
ans.pb({ cy, cx });
for(int dy : {-1, 0, 1}) {
for(int dx : { -1, 0, 1}) {
int y = cy+dy, x = cx+dx;
if(g[y][x] == '1') {
eraseBomb(y, x);
g[y][x] = '0';
}
}
}
}
cout << ans.size() << ENDL;
FOR(i, 0, ans.size()) cout << ans[i].fi << ' ' << ans[i].se << ENDL;
return 0;
}
// ................. main .................. //
void execute() {
#ifdef LOCAL_BOOKNU
auto START_TIME = clock();
#endif
int TTT; cin >> TTT;
FOR(ttt, 0, TTT) cout << "Case #" << ttt+1 << ENDL,
input(), solve();
#ifdef LOCAL_BOOKNU
auto END_TIME = clock();
cout << ENDL << END_TIME - START_TIME << "ms" << ENDL;
#endif
}
int main(void) {
#ifdef LOCAL_BOOKNU
// freopen("input.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
#endif
cin.tie(0), ios_base::sync_with_stdio(false);
execute();
return 0;
}
// ......................................... //