メモ帳

楽しいアウトプットの場所

メモ

標準出力を別ファイルに流し込む方法(Linux)

prr.cppというc++のプログラムを実行し,その標準出力結果をout.txtに上書きします.

g++ -std=gnu++17 prr.cpp && prr > out.txt

ちなみに,これはコマンド実行の結果を別ファイルに流し込むこともできます.例えば,

ls -la > out.txt

のようにlsコマンドで返される結果をout.txtに流し込めます. 「>」のときは上書き 「>>」にすると追記となります.

Node.js について

1.1 ローカルサーバーを立ち上げてHTMLファイルをブラウザに表示させる

ローカルのWebサーバーを立ち上げても、そのwebサイトは自身のPC上でしか閲覧できません。これをインターネット上で、外部から閲覧できるようにします。以下の説明は、Node.jsの環境設定を終わらせているものと仮定して話を進めています。以下のコードはブラウザに表示させるhtmlファイルとなります。ファイル名はaaa.htmlです。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Earth</title>
  </head>

  <body>
    <h2>にゃーん</h2>
    <div class = "tweet_share">
        <font size = "4">ボタンを押して、Twitterでシェアしてみよう!<br></font>
        <a href="https://twitter.com/share?ref_src=twsrc%5Etfw" class="twitter-share-button" data-size="large" data-show-count="false">Tweet</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
    </div>

     <script src = "sarcle.js"></script>
   </body> 
</html> 

javascriptのコードは以下の通りです。ファイル名はapp.jsです。

const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
 
var server = http.createServer();
server.on('request', doRequest);
 
// ファイルモジュールを読み込む
var fs = require('fs');
 
// リクエストの処理
function doRequest(req, res) {
    
    // ファイルを読み込んだら、コールバック関数を実行する。
    fs.readFile('./aaa.html', 'utf-8' , doReard );
    
    // コンテンツを表示する。
    function doReard(err, data) {
        res.writeHead(200, {'Content-Type': 'text/html'});
        res.write(data);
        res.end();
    }
}
 
server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

これらの2つのファイルを同じディレクトリ下において下さい。ファイルを保存したら、ターミナル上でそのディレクトリ下に移動してください。cdコマンドで移動みたいな感じです。

$ node app.js

をターミナルに打ち込みます。私はWindows 10 のwsl2環境で行いました。wslでなくても実行できます。その後以下のように表示されればローカルサーバーの立ち上げに成功です。
ポート番号3000番ですね。その後ブラウザの検索欄で、localhost:3000 と入力して下さい。
f:id:nya__nya:20210504212014p:plain

f:id:nya__nya:20210504212536p:plain
すると、上の画像のように、htmlファイルで記述したものがブラウザ上に表示されます。しかし、これはローカル環境でしか反映されません。次に、インターネット上で誰でもこのwebページにアクセスできるようにします。

1.2 ngrokでローカル環境外からでも閲覧可能にする

https://ngrok.com/
上のサイトからngrokを任意のディレクトリにインストールして、解凍してください。すると、ngrok.exeという実行ファイルが出現します。それを起動させ、

ngrok http 3000

と打ち込むと外部公開できるようになります。

f:id:nya__nya:20210504213547p:plain
上の図のForwardingという欄の後に記述されているものが、アクセスできるurlとなります。
Ctrl+C のキーを押すと外部公開終了となり、urlにアクセスできなくなります。ちなみに、このurlは毎回異なる値となります。

2-1 expressでhttpサーバーを作る

この記事が分かりやすかったので参考にしました。
blog.katsubemakito.net
以下のコードは、webサイトにアクセスがあった時に、Terminal上でログ出力するというものです。サーバー管理用のserv.jsとブラウザ表示用のaaa.htmlを同じディレクトリ下に置いています。

const express = require("express");
const app  = express();
const port = 3000;

// ルーティングの設定
app.get("/", (req, res) =>{
  res.sendFile(`${__dirname}/aaa.html`);
  console.log("/ へアクセスがありました");
});

// HTTPサーバを起動する
app.listen(port, () => {
  console.log(`listening at http://localhost:${port}`);
});

アクセスカウンタを実装したコードは以下の通り。localhost:3000/resetのurlにアクセスするとアクセスカウンタの数字がリセットされる。

  1. サーバ起動中はグローバル変数は生き続ける
  2. サーバが死ぬとグローバル変数も死ぬ
const express = require("express");
const app  = express();
const port = 3000;

// カウンター用のデータ
let COUNT = 1;

// ルーティング
app.get("/", (req, res)=>{

  res.send(`あなたは${COUNT}人目のお客様です`);
  COUNT++;
});
app.get("/reset", (req, res)=>{
  res.send(`カウントをリセットしました`);
  COUNT=1;
});

// サーバを起動
app.listen(port, ()=>{
  console.log(`Running at http://localhost:${port}/`);
})

メッセージ送信フォームを作る

ファイル構成

|-document root
    |  app.js
    |  file1.txt
    |---views
           index.ejs

クライアント側から入力された情報を、コンソール画面に出力し、file1.txtに追記するというシステムです。非同期処理で実装しているため、高速です。
Body Parser(body-parser)でPOST処理をおこなう[フォーム]

const express = require("express")
const ejs = require('ejs')
const bodyParser = require("body-parser")
const app = express()
const fs = require("fs");
var data = "yet";
const port = 3000;
 
app.set('ejs', ejs.renderFile)
 
app.use(bodyParser.urlencoded({ extended: false }))
 
app.get("/", (req, res) => {
    res.render("./index.ejs",
        {
            name: '未送信',
            message: '未送信'
        })
});

app.post("/", (req, res) => {
    res.render('index.ejs', {
        name: req.body.name,
        message: req.body.message
    })

    data = req.body.name+"さん : "+req.body.message;
    fs.appendFile("file1.txt", data+'\n', (err) => {
        if(err){
            console.log(`[error] ${err}`);
        }
        console.log(data);
    });
});


app.listen(port, () => {
  console.log(`listening at http://localhost:${port}`);
});
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <style>
        .size-input-name {width: 16em; height: 2em;}
        .size-input-message {width: 50em; height: 3em; }
    </style>
</head>
<body>
    <h2>メッセージ送信フォーム</h2>
    <br>
    <form action="/" method="post">
        <input type="text" name="name" placeholder="名前" class = "size-input-name">
        <hr>
        <textarea type="text" name = "message" rows = "10" cols = "80" placeholder="メッセージ"></textarea>
        <hr>
        <button type="submit" name="button">送信</button>
    </form>
    <br>
    <h2>送信内容</h2>
    <ul>
        <li>名前:<%=name %></li>
        <li>メッセージ:<%=message %></li>
    </ul>
</body>
</html>

ABC200-D

D - Message from Aliens

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

int main(){
    string s;
    cin >> s;
    deque<char> dq;
    ll n = s.size();
    ll flag = 1;
    for(ll i=0; i<n; i++){
        if(s[i] == 'R'){
            flag*=-1;
        }else{
            if(flag == 1){
                if(!dq.empty() && s[i] == dq.back()) dq.pop_back();
                else dq.push_back(s[i]);
            }else{
                if(!dq.empty() && s[i] == dq.front()) dq.pop_front();
                else dq.push_front(s[i]);
            }
        }
    }
    string T = "";
    for(char x : dq) T+=x;
    if(flag == -1) reverse(T.begin(), T.end());
    cout << T << endl;

    return 0;
}

感想

先頭要素や末尾の要素を削除するために、sutstrやdeleteを使うとo(n)くらいかかるため、時間が間に合わない。デキューは、末尾先頭への要素の追加削除,参照をo(1)で行うため、この問題を解くことに適している。それから、デキューが空の状態で中身を参照すると、配列外参照となるため、以下のようにして対応する。!dq.empy()が偽の時それ以降のs[i] == dq.back()が行われない。逆に左右を逆にすると最初に、dq.back()の参照が行われてしまい、配列外参照のエラーになる恐れがあるため、必ず左側に!dq.empty()を書く必要がある。

 if(!dq.empty() && s[i] == dq.back()) dq.pop_back();

Pythonのメモ帳

値の宣言

a=10, b=20を一行で宣言する

a, b = 10, 20
値の交換

例えば、a=2, b=5の入力を与えると、a=5, b=2になる

a, b = map(int, input().split())
a, b = b, a
配列

昇順ソート

a.sort()
a = sorted(a)

降順ソート

a.sort(reverse = True)
a = sorted(a, reverse = True)

リスト内包表記
1行目の表記では、if文で真と判定されたもののみが配列に入れられる。

even_numbers = [x for x in range(11) if x%2 == 0]
squares = [x*x for x in range(5)]
even_squares = [x*x for x in even_numbers]

print(even_numbers)
print(squares)
print(even_squares)

出力

[0, 2, 4, 6, 8, 10]
[0, 1, 4, 9, 16]
[0, 4, 16, 36, 64, 100]
真偽値

これら8つの値以外は全て真(True)として読みとられる。

if not 0: print("Yes1")
if not 0.0: print("Yes2")
if not []: print("Yes3")
if not {}: print("Yes4")
if not "": print("Yes5")
if not None: print("Yes6")
if not False: print("Yes7")
if not set(): print("Yes8")

出力

Yes1
Yes2
Yes3
Yes4
Yes5
Yes6
Yes7
Yes8

例えば文字列"aaaaa"を突っ込むと、真として判定される

if "aaaaa": print("Yes") #Yes

木構造の最短経路問題 ABC-70D Transit Tree Path

問題文

N頂点の木が与えられます。
木とはグラフの一種であり、頂点の数をNとすると、辺の数がN-1本である閉路の無い連結グラフです。
i(1\leq i \leq N-1)番目の辺は頂点a_iと頂点b_iを距離c_iで結びます。
また、Q個の質問クエリと整数Kが与えらえます。
j(1\leq j \leq Q)番目の質問クエリでは、頂点x_jから頂点Kを経由しつつ、頂点y_jまで移動する場合の最短経路の距離を求めて下さい。
atcoder.jp

考えたこと

  1. 頂点xから頂点yへの頂点Kを経由する場合の最短経路は、頂点Kからxへの最短経路と頂点Kからyへの最短経路の和で求まる。
  2. 最初ダイクストラ法で実装しようとしたが、よく考えたら、閉路が無い木構造であり、ある頂点からある頂点までの経路が一つしかないため、深さ優先探索(dfs)で探索すれば十分であることが分かった。
  3. 辺の数がn-1なので、間違えてforループをn回すとバグらせるので注意が必要。
const int limit = 100010;
using edge = struct{ll to; ll cost;};
vector<edge> tree[limit];
ll depth[limit];


void dfs(ll v, ll p, ll d){
    depth[v] = d;
    for(auto &e : tree[v]){
        if(e.to == p) continue;
        dfs(e.to, v, d+e.cost);
    }
}

int main() {
    ll n; cin >> n;
    for(ll i=0; i<n-1; i++){
        ll a, b, c; cin >> a >> b >> c;
        tree[a].push_back({b, c});
        tree[b].push_back({a, c});
    }

    ll Q, K; cin >> Q >> K;
    dfs(K, -1, 0);

    for(ll i=0; i<Q; i++){
        ll x, y; cin >> x >> y;
        cout << depth[x]+depth[y] << '\n';
    }


    return 0;
}

蟻本p34 部分和問題 深さ優先探索をNimで解く

問題

整数a_1, a_2, ..., a_nが与えられます。その中からいくつか選び、その和をちょうどkにすることができるか判定しなさい。

入出力

n = 4\\
a = {1, 2, 4, 7}\\
k = 13\\
\rightarrow Yes(13 = 2+4+7)\\
n = 4\\
a = {1, 2, 4, 7}\\
k = 15\\
\rightarrow No

コード

import sequtils, strutils, strformat, algorithm, math, sugar, complex
{.warning[UnusedImport]: off.}

var n, k: int
var a = newseq[int]()

proc dfs(i:int, sum:int): bool = 
    if i == n: return sum == k

    if dfs(i+1, sum): return true 
    if dfs(i+1, sum+a[i]): return true
    return false

n = stdin.readLine.parseInt()
a = stdin.readLine.split.map(parseInt)
k = stdin.readLine.parseInt()

if dfs(0, 0): echo "Yes"
else: echo "No"