Helping Hiasat  (Codeforces 1105E)
Tags: DP, meet-in-the-middle

Hiasat registered a new account in NeckoForces and when his friends found out about that, each one of them asked to use his name as Hiasat's handle.Luckily for Hiasat, he can change his handle in some points in time. Also he knows the exact moments friends will visit his profile page. Formally, you are given a sequence of events of two types:  $$$1$$$ — Hiasat can change his handle.  $$$2$$$ $$$s$$$ — friend $$$s$$$ visits Hiasat's profile. The friend $$$s$$$ will be happy, if each time he visits Hiasat's profile his handle would be $$$s$$$.Hiasat asks you to help him, find the maximum possible number of happy friends he can get.

Input: The first line contains two integers $$$n$$$ and $$$m$$$ ($$$1 \le n \le 10^5, 1 \le m \le 40$$$) — the number of events and the number of friends.Then $$$n$$$ lines follow, each denoting an event of one of two types:   $$$1$$$ — Hiasat can change his handle.  $$$2$$$ $$$s$$$ — friend $$$s$$$ ($$$1 \le |s| \le 40$$$) visits Hiasat's profile. It's guaranteed, that each friend's name consists only of lowercase Latin letters.It's guaranteed, that the first event is always of the first type and each friend will visit Hiasat's profile at least once.

Output: Print a single integer — the maximum number of happy friends.

Note: In the first example, the best way is to change the handle to the "motarack" in the first event and to the "light" in the fourth event. This way, "motarack" and "light" will be happy, but "mike" will not.In the second example, you can choose either "alice", "bob" or "tanyaromanova" and only that friend will be happy.

Sample Inputs:
5 3
1
2 motarack
2 mike
1
2 light

4 3
1
2 alice
2 bob
2 tanyaromanova

Sample Outputs:
2

1

Solution (Python):
import time

def find_max_clique(remain, size, max_, index, maxs):
    result = max_
    if size + len(remain) <= result:
        return result
    if not remain:
        return size
    while remain:
        candidate = max(remain)
        if maxs[candidate] + size <= result:
            return result
        if size + len(remain) <= result:
            return result
        remain.remove(candidate)
        sub_result = find_max_clique(remain & index[candidate], size + 1, result, index, maxs)
        if sub_result > result:
            result = sub_result
            return result
    return result

def test_find():
    index = [{2, 4, 5, 7}, {4, 5, 6}, {0, 5, 6, 7}, {5, 6, 7}, {0, 1, 6, 7}, {0, 1, 2, 3}, {1, 2, 3, 4}, {0, 2, 3, 4}]
    m = 8
    maxs = [0] * m
    whole = set()
    for i in range(m):
        whole.add(i)
        maxs[i] = max(maxs[i - 1], find_max_clique(whole & index[i], 1, maxs[i - 1], index, maxs))

def solve(events, m):
    index = [set() for _ in range(m)]
    r = []
    while events:
        ele = events.pop()
        if ele is None:
            r.clear()
        else:
            for n in r:
                index[n].add(ele)
            index[ele].update(r)
            r.append(ele)
    whole = set(range(m))
    for i in range(m):
        index[i] = whole - index[i] - {i}
    maxs = [0] * m
    whole = set()
    for i in range(m):
        whole.add(i)
        maxs[i] = max(maxs[i - 1], find_max_clique(whole & index[i], 1, maxs[i - 1], index, maxs))
    return maxs[-1]

def test():
    events = []
    m = 700
    for i in range(m):
        events.extend([None, i])
    tick = time.time()
    print(solve(events, m))
    tock = time.time()
    print('T:', round(tock - tick, 5))

def main():
    (n, m) = map(int, input().split())
    events = []
    d = {}
    id_ = 0
    for i in range(n):
        line = input()
        if line.startswith('1'):
            events.append(None)
        else:
            if line not in d:
                d[line] = id_
                id_ += 1
            events.append(d[line])
    print(solve(events, m))
if __name__ == '__main__':
    main()

Solution (Python):
import random
import sys, os, io
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
(n, m) = map(int, input().split())
d = dict()
(c, f) = (0, 0)
ng = [[0] * m for _ in range(m)]
for _ in range(n):
    s = input().rstrip()
    if len(s) == 1:
        if f:
            for i in se:
                for j in se:
                    if i ^ j:
                        ng[i][j] = 1
                        ng[j][i] = 1
        f = 1
        se = set()
    elif len(s) ^ 1 and f:
        if not s in d:
            d[s] = c
            c += 1
        se.add(d[s])
if f:
    for i in se:
        for j in se:
            if i ^ j:
                ng[i][j] = 1
                ng[j][i] = 1
G = [[] for _ in range(m)]
for i in range(m):
    ngi = ng[i]
    for j in range(m):
        if ngi[j]:
            G[i].append(j)
ans = 0
p = [i for i in range(m)]
for _ in range(1000):
    (ok, c) = ([1] * m, 0)
    random.shuffle(p)
    for i in p:
        if not ok[i]:
            continue
        c += 1
        for j in G[i]:
            ok[j] = 0
        ok[j] = 0
    ans = max(ans, c)
print(ans)

Solution (Python):
import time
(n, m) = map(int, input().split())
names = {None: 0}
count = {i: 0 for i in range(1, m + 1)}
a = [0] * (n + 1)
for i in range(n):
    l = list(input().split())
    if l[0] == '2':
        if l[1] not in names:
            names[l[1]] = len(names)
        a[i] = names[l[1]]
        count[a[i]] = count[a[i]] + 1
dense = [set() for _ in range(m + 1)]
bulk = set()
for i in range(n):
    if a[i] == 0:
        bulk.clear()
        continue
    if a[i] in bulk:
        continue
    for j in bulk:
        dense[j].add(a[i])
        dense[a[i]].add(j)
    bulk.add(a[i])
res = 0
happy = set()
unhappy = set()
mindep = 99
independent = set()
dependent = set()
for i in range(1, m + 1):
    if len(dense[i]) == 0:
        independent.add(i)
    else:
        dependent.add(i)
        if len(dense[i]) < mindep:
            mindep = len(dense[i])
for k in list(dependent):
    happy.clear()
    unhappy.clear()
    bulk.clear()
    bulk.update(dependent)
    happy.add(k)
    bulk.remove(k)
    for j in dense[k]:
        if j in bulk:
            unhappy.add(j)
            bulk.remove(j)
    while bulk:
        mini = min([len(dense[i]) for i in bulk])
        for i in list(bulk):
            if len(dense[i]) == mini and i in bulk:
                happy.add(i)
                bulk.remove(i)
                for j in dense[i]:
                    if j in bulk:
                        unhappy.add(j)
                        bulk.remove(j)
                continue
    res = max(res, len(happy))
print(res + len(independent))