You'll need to implement something like this:
pragma solidity 0.4.26;
import './IERC20Token.sol';
contract Airdrop {
address public owner;
mapping(address => uint256) public balances;
constructor() public {
owner = msg.sender;
}
function transferBatch(IERC20Token _token, address[] _targets, uint256[] _amounts) external {
require(msg.sender == owner);
uint256 length = _targets.length;
require(length == _amounts.length);
for (uint256 i = 0; i < length; i++) {
address target = _targets[i];
uint256 amount = _amounts[i];
require(balances[target] == 0);
require(_token.transfer(target, amount));
balances[target] = amount;
}
}
}
Then you'll need to deploy this contract and transfer to it the entire amount that you want to distribute.
Finally, you'll need to determine how many addresses you want to transfer to in a single transaction.
The more addresses per transaction, the lower your total cost will be.
However, you cannot surpass the block gas-limit per transaction.
Moreover, in order to optimize the speed of execution, you would want to allow miners to squeeze more than a single transaction into one block. So, for example, if the average size of a transaction is a little more than 1/2 of the block gas-limit, then miners will not be able to squeeze more than one transaction into each block; if the average size of a transaction is a little more than 1/3 of the block gas-limit, then miners will not be able to squeeze more than two transactions into each block; and so forth.
So you'll need to do some experiments (on Ganache, for example, or better yet - for accuracy reasons - on Ropsten), in order to find an optimal batch-size.
From personal experience, 100 addresses per batch should be slightly less than 1/3 of the block gas-limit, so you might wanna use this as your batch size.
UPDATE:
You'll be able to save a significant amount of gas (thus reducing the overall cost), by removing the balances
variable; however, note that this variable is there to protect you from distributing more than once per each address (checking the actual balance of that address is useless for this purpose, since its owner can easily transfer his/her tokens from this address to some other address).