#include <iostream>
#include <sstream>
#include <vector>
#include <iomanip>
#include <algorithm>

struct TestData
{
  int testNum;
  std::string input;
  std::string expectedOutput;
};

struct TestResult
{
  std::string testOutput;
  bool testPassed;
};

void testSolution();
TestResult runTestCase(const TestData& testData);
std::string formatResult(const TestData& testData, const TestResult& testResult);
void solution(std::istream& is, std::ostream& os);
int countConsecutiveString(const std::vector<std::string>& collection, int startPos);
bool isEqual(const std::string& str1, const std::string& str2);
long long int getNumPairs(long long int elementCount);

static const bool IS_TESTING = false;

// define test data
static const std::vector<TestData> testDatas = {
  {1, "3\nacornistnt\npeanutbomb\nconstraint\n", "1\n"},
  {2, "2\noneplustwo\nninemodsix\n", "0\n"},
  {3, "5\nabaaaaaaaa\noneplustwo\naaaaaaaaba\ntwoplusone\naaaabaaaaa\n", "4\n"},
  {4, "2\nabaaaaaaaa\naaaaaaaabb\n", "0\n"},
};

int main()
{
  if (IS_TESTING)
    testSolution();
  else
    solution(std::cin, std::cout);

  return 0;
}

void testSolution()
{
  for (const auto& testData : testDatas) {
    auto testResult = runTestCase(testData);

    std::cout << formatResult(testData, testResult);
  }
}

TestResult runTestCase(const TestData& testData)
{
    std::istringstream iss {testData.input};
    std::ostringstream oss {};

    solution(iss, oss);

    auto testOutput = oss.str();
    auto testPassed = testData.expectedOutput.compare(testOutput) == 0;
    
    return {testOutput, testPassed};
}

std::string formatResult(const TestData& testData, const TestResult& testResult)
{
  std::ostringstream oss {};

  oss << "Test " << std::setw(2) << testData.testNum << ": "
    << (testResult.testPassed ? "Passed!" : "Failed!") << std::endl;
  
  oss << "\tExpected: " << testData.expectedOutput << "\tActual  : " << testResult.testOutput << std::endl;

  return oss.str();
}

// solution to the problem
void solution(std::istream& is, std::ostream& os)
{
  int numString; is >> numString;

  auto inputStrings = std::vector<std::string>(numString);
  for (auto i = 0; i < numString; ++i) {
    is >> inputStrings.at(i);
  }

  // prep
  for (auto i = 0; i < numString; ++i) {
    auto& str = inputStrings[i];
    std::sort(str.begin(), str.end());
  }
  std::sort(inputStrings.begin(), inputStrings.end());

  long long int pairCount = 0;
  for (auto i = 0; i < inputStrings.size();) {
    auto anagramCount = countConsecutiveString(inputStrings, i);
    pairCount += getNumPairs(anagramCount);
    i += anagramCount;
  }

  os << pairCount << std::endl;
}

int countConsecutiveString(const std::vector<std::string>& collection, int startPos)
{
  const auto& reference = collection[startPos];
  int consecutiveCount = 1;
  
  for (auto i = startPos + 1; i < collection.size(); ++i) {
    const auto& compareAgainst = collection[i];
    
    if (!isEqual(reference, compareAgainst))
      break;
   
    ++consecutiveCount;
  }

  return consecutiveCount;
}

bool isEqual(const std::string& str1, const std::string& str2)
{
  return str1.compare(str2) == 0;
}

long long int getNumPairs(long long int elementCount)
{
  return (elementCount * (elementCount - 1)) / 2;
}

Measured memory_bytes: 7296