結果
64:08 5 完 1255 位 です。(はい!?!?)
まさか ABC で冷えるとは思っていませんでした。 F の全方位で詰まったのは、(突然書き方を変えたのもありますが)実力相応で、戦犯は D の制約読み落としです。
✔A - Coffee (00:02:32)
string で受け取って比較です。
遅かったのは、online-judge-tools を初めて AtCoder でためしてうまくいかなかったからですから、良いとしましょう。 コンテスト中は使えないなどあるのでしょうか。
std::string s;std::cin>>s; std::cout<<(s.at(2)==s.at(3)&&s.at(4)==s.at(5)?"Yes":"No")<<'\n';
✔B - Golden Coins (00:04:00)
500 円を作れるだけ作って、残りで 5 円を作りましょう。 予め入力を 5 で割っておくと良いです。
lint n;std::cin>>n;n/=5; std::cout<<n*5+n/100*500<<'\n';
✔C - Traveling Salesman around Lake (00:45:29 + (1))
WA になっているのに気づかずです。(1 回目は 00:05:44)
最も長い場所を探しましょう。 vector で受け取って差を見ます。 最初と最後の距離は、最初のものに 1 周分足したと思うと良いです。
lint k,n;std::cin>>k>>n; std::vector<lint>a(n); for(lint&x:a)std::cin>>x; lint max=k+a.front()-a.back(); for(lint i=0;i<n-1;i++){ lint x=a.at(i+1)-a.at(i); if(max<x)max=x; } std::cout<<max<<'\n';
✔D - Line++ (00:43:16)
N は 2,000 以下です。(最重要事項)
これがないと地獄場合分けなのですが、焼け野原になっているであろう順位表を覗きに伺ったところ、 傷一つないご様子でしたから、一応、念の為、制約をチェックです。まさか私が制約を読まないなんてそんな…… をしていました(泣)
すべての点対に関して最短距離を定数時間でもとめて、重複度付きチェックリストを作ると良いです。 最短距離は、もとのグラフの最短距離と、X, Y を通るときのものを比較すると良いです。
lint n,x,y;std::cin>>n>>x>>y; std::vector<lint>a(n); for(lint i=1;i<=n;i++){ for(lint j=i+1;j<=n;j++){ a.at(std::min({ j-i, std::abs(i-x)+1+std::abs(y-j), std::abs(i-y)+1+std::abs(x-j) }))++; } } for(lint k=1;k<n;k++){ std::cout<<a.at(k)<<'\n'; }
✔E - Red and Green Apples (00:59:08)
赤色と緑色は始めから X 個、Y 個しかないとしてよいです。 すると、どう選んでも制約は満たされますから、大き方から X + Y 個選ぶと良いです。
実装は怒涛の標準ライブラリさんです。
lint X,Y,A,B,C;std::cin>>X>>Y>>A>>B>>C; std::vector<lint>a(A),b(B),c(C); for(lint&x:a)std::cin>>x; for(lint&x:b)std::cin>>x; for(lint&x:c)std::cin>>x; std::sort(a.begin(),a.end(),std::greater<>{}); std::sort(b.begin(),b.end(),std::greater<>{}); std::sort(c.begin(),c.end(),std::greater<>{}); std::vector<lint>d; d.insert(d.end(),a.begin(),a.begin()+X); d.insert(d.end(),b.begin(),b.begin()+Y); d.insert(d.end(),c.begin(),c.end()); std::sort(d.begin(),d.end(),std::greater<>{}); std::cout<<std::accumulate(d.begin(),d.begin()+X+Y,0ll)<<'\n';
✖F - Distributing Integers
全方位です。
根 x に隣接する各の頂点 y に関して、部分木のサイズ sz[y] と、 部分木の着色順の場合の数 dp[y] が分かっているとしましょう。 すると、根からの着色順の場合の数は、(sz[x] - 1) ! Π dp[y] / sz[y]! です。 これを全方位にするとよいです。
lint n;std::cin>>n; std::vector<std::vector<lint>>g(n); for(lint i=0;i<n-1;i++){ lint u,v;std::cin>>u>>v;u--,v--; g.at(u).push_back(v); g.at(v).push_back(u); } std::vector<lint>ord; auto dfs=[&](auto&&dfs,lint x,lint p)->void{ ord.push_back(x); auto&&v=g.at(x); for(lint y:v)if(p!=y)dfs(dfs,y,x); if(x)v.erase(std::find(v.begin(),v.end(),p)); }; dfs(dfs,0,0); auto rord=std::vector<lint>(ord.rbegin(),ord.rend()); std::vector<lint>sz(n,1); std::vector<mint>fact(n+1);fact.at(0)=mint{1}; for(lint i=1;i<=n;i++)fact.at(i)=fact.at(i-1)*mint{i}; std::vector<mint>dp(n),ep(n);ep.at(0)=mint{1}; for(lint x:rord){ for(lint y:g.at(x))sz.at(x)+=sz.at(y); dp.at(x)=fact.at(sz.at(x)-1); for(lint y:g.at(x))dp.at(x)*=dp.at(y)/fact.at(sz.at(y)); } for(lint x:ord){ for(lint y:g.at(x)){ ep.at(y)=dp.at(x)*ep.at(x)*fact.at(n-sz.at(y)-1)*fact.at(sz.at(y)) /(dp.at(y)*fact.at(sz.at(x)-1)*fact.at(n-sz.at(x))); } } for(lint i=0;i<n;i++){ std::cout<<(dp.at(i)*ep.at(i)*fact.at(n-1) /(fact.at(sz.at(i)-1)*fact.at(n-sz.at(i)))).value<<'\n'; }
長いです。
今回の実装では、keymoon さんから教わったこちらを導入してみました。 コンテスト 86 分前に敵に塩を送るなんて、お人好しです。(ところでまったく勝負になっていないのですが、あの……)
一回目で帰りがけ順を記録しておけば、二回目はその逆をするだけなのでDFSをするのは一回で良いみたいな説はあります(実質2回なんですが…)
— keymoon (@kymn_) March 28, 2020