#include <bits/stdc++.h>
using namespace std;
#define ll long long int
#define ld long double
#define pb push_back
#define mp make_pair
#define F first
#define S second
#define fast ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
typedef pair<ll, ll> pli;
typedef pair<int,int> pii;
typedef pair<ld,ld> pld;
#define PI 3.14159265
ll MOD=998244353;
int MX=1e9;
ld pi=3.1415926535;

int a[105];
int solve(int x,int i)
{

}
int main()
{
    fast;

    //freopen("powers.in","r",stdin);
    //cout<<"? ";
    printf("? ");
    for(int i=0;i<14;i++)
    {
        int z=(1<<i);
        //cout<<z<<" ";
        printf("%d ",z);
    }
    for(int i=pow(2,12)+1,j=1;j<=86;j++,i++)
    {
        //cout<<i;
        printf("%d",i);
        if(j<86)
        {
            //cout<<" ";
            printf(" ");
        }
        if(j==86)
        {
            //cout<<endl;
            printf("\n");
        }
    }
    fflush(stdout);
    for(int i=1;i<=100;i++)
    {
        scanf("%d",&a[i]);
        //cin>>a[i];
    }
    ll res=0;
    for(int i=1;i<=14;i++)
    {
        int x=((1<<(i-1))&a[i]);
        if(x!=0)
        {
            res+=(1<<(i-1));
        }
    }
    //cout<<"! "<<res<<"\n";
    printf("! %d\n",res);
    fflush(stdout);
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define vd(T) vector<vector<T>>
#define v(T) vector<T>
#define mod (ll)(1e9+7)
#define pp(T) pair<T,T>
#define pb push_back
#define read1(x) scanf("%d",&x)
#define read2(x,y) scanf("%d%d",&x,&y)
#define read3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define read4(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
#define mp make_pair
#define NN 100005
#define IO ios_base::sync_with_stdio(NULL),cin.tie(0),cout.tie(0)
#define loop(i,a,b) for(ll i=a;i<b;i++)
#define DEBUG cout<<"***********DEBUGGING***********"<<endl
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
typedef tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update> ordered_set;
ll power(ll a,ll b){ if(b==0) return 1; ll s=power(a,b/2); s*=s;if(b&1) return s*a; return s;}
ll powermod(ll a,ll b,ll m){ if(b==0) return 1; ll s=power(a,b/2);s*=s; s%=m; if(b&1) return (s*a)%m; return s;}
inline int add(ll a,ll b,ll m=0){
    if(m==0) return a+b;
    return (a%m+b%m)%m;
}

inline int sub(ll a,ll b ,ll m=0){
    if(m==0) return a-b;
    return ((a%m-b%m)%m+m)%m;
}

inline int mul(ll a,ll b,ll m=0){
    if(m==0)return (a*b);
    return ((a%m)*(b%m))%m;
}

inline int div(ll a,ll b,ll m=0){
    if(m==0) return a/b;
    return ((a%m)*(powermod(a,b,m-2)%m))%m;
}

bool isopen(char c){
    return ((c=='(')||(c=='{')||(c=='['));
}

ll longest_balanced_parenthesis(string a,ll n){
    stack<char> s;
    ll start=0;
    ll ans=0,m=0;
    for(ll i=0;i<n;i++){
        if(isopen(a[i])){
            s.push(a[i]);
        }
        else{
            if(isopen(a[i])|| s.empty()){
                m=max(m,ans);
                continue;
            }
            else{
                s.pop();
                ans+=2;
                m=max(ans,m);
            }
        }
    }

    return m;

}

ll setup_k(ll *a,ll k){
  ordered_set x;
  // Now , insert and then check number of integers less than k by just using x.order_of_key(k+1)
  return 0;
}

void f(){
    fflush(stdin);
}

int main() {

    IO;

    ll a=0;
    for(ll i=0;i<100;i++){
        cout<<i<<" ";
    }
    f();
    cin>>a;
    ll l=(1<<7)-1;
    a&=(l*(1<<7));
    for(ll i=128;i<238;i++){
        cout<<i<<" ";
    }
    f();
    ll b;
    cin>>b;
    b&=((1<<7)-1);
    a^=b;
    cout<<a<<endl;

}


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define vd(T) vector<vector<T>>
#define v(T) vector<T>
#define mod (ll)(1e9+7)
#define pp(T) pair<T,T>
#define pb push_back
#define read1(x) scanf("%d",&x)
#define read2(x,y) scanf("%d%d",&x,&y)
#define read3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define read4(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
#define mp make_pair
#define NN 100005
#define IO ios_base::sync_with_stdio(NULL),cin.tie(0),cout.tie(0)
#define loop(i,a,b) for(ll i=a;i<b;i++)
#define DEBUG cout<<"***********DEBUGGING***********"<<endl
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
typedef tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update> ordered_set;
ll power(ll a,ll b){ if(b==0) return 1; ll s=power(a,b/2); s*=s;if(b&1) return s*a; return s;}
ll powermod(ll a,ll b,ll m){ if(b==0) return 1; ll s=power(a,b/2);s*=s; s%=m; if(b&1) return (s*a)%m; return s;}
inline int add(ll a,ll b,ll m=0){
    if(m==0) return a+b;
    return (a%m+b%m)%m;
}

inline int sub(ll a,ll b ,ll m=0){
    if(m==0) return a-b;
    return ((a%m-b%m)%m+m)%m;
}

inline int mul(ll a,ll b,ll m=0){
    if(m==0)return (a*b);
    return ((a%m)*(b%m))%m;
}

inline int div(ll a,ll b,ll m=0){
    if(m==0) return a/b;
    return ((a%m)*(powermod(a,b,m-2)%m))%m;
}

bool isopen(char c){
    return ((c=='(')||(c=='{')||(c=='['));
}

ll longest_balanced_parenthesis(string a,ll n){
    stack<char> s;
    ll start=0;
    ll ans=0,m=0;
    for(ll i=0;i<n;i++){
        if(isopen(a[i])){
            s.push(a[i]);
        }
        else{
            if(isopen(a[i])|| s.empty()){
                m=max(m,ans);
                continue;
            }
            else{
                s.pop();
                ans+=2;
                m=max(ans,m);
            }
        }
    }

    return m;

}

ll setup_k(ll *a,ll k){
  ordered_set x;
  // Now , insert and then check number of integers less than k by just using x.order_of_key(k+1)
  return 0;
}

void f(){
    fflush(stdout);
}

int main() {

    IO;

    ll a=0;
    cout<<"?"<< "";
    for(ll i=0;i<100;i++){
        cout<<i<<" ";
    }
    cout<<endl;
    f();
    cin>>a;
    cout<<"?"<< "";
    ll l=(1<<7)-1;
    a&=(l*(1<<7));
    for(ll i=128;i<238;i++){
        cout<<i<<" ";
    }
    cout<<endl;
    f();
    ll b;
    cin>>b;
    b&=((1<<7)-1);
    a^=b;
    cout<<"!";
    cout<<a<<endl;

}


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define vd(T) vector<vector<T>>
#define v(T) vector<T>
#define mod (ll)(1e9+7)
#define pp(T) pair<T,T>
#define pb push_back
#define read1(x) scanf("%d",&x)
#define read2(x,y) scanf("%d%d",&x,&y)
#define read3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define read4(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
#define mp make_pair
#define NN 100005
#define IO ios_base::sync_with_stdio(NULL),cin.tie(0),cout.tie(0)
#define loop(i,a,b) for(ll i=a;i<b;i++)
#define DEBUG cout<<"***********DEBUGGING***********"<<endl
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
typedef tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update> ordered_set;
ll power(ll a,ll b){ if(b==0) return 1; ll s=power(a,b/2); s*=s;if(b&1) return s*a; return s;}
ll powermod(ll a,ll b,ll m){ if(b==0) return 1; ll s=power(a,b/2);s*=s; s%=m; if(b&1) return (s*a)%m; return s;}
inline int add(ll a,ll b,ll m=0){
    if(m==0) return a+b;
    return (a%m+b%m)%m;
}

inline int sub(ll a,ll b ,ll m=0){
    if(m==0) return a-b;
    return ((a%m-b%m)%m+m)%m;
}

inline int mul(ll a,ll b,ll m=0){
    if(m==0)return (a*b);
    return ((a%m)*(b%m))%m;
}

inline int div(ll a,ll b,ll m=0){
    if(m==0) return a/b;
    return ((a%m)*(powermod(a,b,m-2)%m))%m;
}

bool isopen(char c){
    return ((c=='(')||(c=='{')||(c=='['));
}

ll longest_balanced_parenthesis(string a,ll n){
    stack<char> s;
    ll start=0;
    ll ans=0,m=0;
    for(ll i=0;i<n;i++){
        if(isopen(a[i])){
            s.push(a[i]);
        }
        else{
            if(isopen(a[i])|| s.empty()){
                m=max(m,ans);
                continue;
            }
            else{
                s.pop();
                ans+=2;
                m=max(ans,m);
            }
        }
    }

    return m;

}

ll setup_k(ll *a,ll k){
  ordered_set x;
  // Now , insert and then check number of integers less than k by just using x.order_of_key(k+1)
  return 0;
}

void f(){
    fflush(stdout);
}

int main() {

    IO;

    ll a=0;
    cout<<"?"<< "";
    for(ll i=0;i<100;i++){
        cout<<i<<" ";
    }
    f();
    cin>>a;
    cout<<"?"<< "";
    ll l=(1<<7)-1;
    a&=(l*(1<<7));
    for(ll i=128;i<238;i++){
        cout<<i<<" ";
    }
    f();
    ll b;
    cin>>b;
    b&=((1<<7)-1);
    a^=b;
    cout<<"!";
    cout<<a<<endl;

}


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define vd(T) vector<vector<T>>
#define v(T) vector<T>
#define mod (ll)(1e9+7)
#define pp(T) pair<T,T>
#define pb push_back
#define read1(x) scanf("%d",&x)
#define read2(x,y) scanf("%d%d",&x,&y)
#define read3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define read4(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
#define mp make_pair
#define NN 100005
#define IO ios_base::sync_with_stdio(NULL),cin.tie(0),cout.tie(0)
#define loop(i,a,b) for(ll i=a;i<b;i++)
#define DEBUG cout<<"***********DEBUGGING***********"<<endl
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
typedef tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update> ordered_set;
ll power(ll a,ll b){ if(b==0) return 1; ll s=power(a,b/2); s*=s;if(b&1) return s*a; return s;}
ll powermod(ll a,ll b,ll m){ if(b==0) return 1; ll s=power(a,b/2);s*=s; s%=m; if(b&1) return (s*a)%m; return s;}
inline int add(ll a,ll b,ll m=0){
    if(m==0) return a+b;
    return (a%m+b%m)%m;
}

inline int sub(ll a,ll b ,ll m=0){
    if(m==0) return a-b;
    return ((a%m-b%m)%m+m)%m;
}

inline int mul(ll a,ll b,ll m=0){
    if(m==0)return (a*b);
    return ((a%m)*(b%m))%m;
}

inline int div(ll a,ll b,ll m=0){
    if(m==0) return a/b;
    return ((a%m)*(powermod(a,b,m-2)%m))%m;
}

bool isopen(char c){
    return ((c=='(')||(c=='{')||(c=='['));
}

ll longest_balanced_parenthesis(string a,ll n){
    stack<char> s;
    ll start=0;
    ll ans=0,m=0;
    for(ll i=0;i<n;i++){
        if(isopen(a[i])){
            s.push(a[i]);
        }
        else{
            if(isopen(a[i])|| s.empty()){
                m=max(m,ans);
                continue;
            }
            else{
                s.pop();
                ans+=2;
                m=max(ans,m);
            }
        }
    }

    return m;

}

ll setup_k(ll *a,ll k){
  ordered_set x;
  // Now , insert and then check number of integers less than k by just using x.order_of_key(k+1)
  return 0;
}

void f(){
    fflush(stdout);
}

int main() {

    IO;

    ll a=0;
    for(ll i=0;i<100;i++){
        cout<<i<<" ";
    }
    f();
    cin>>a;
    ll l=(1<<7)-1;
    a&=(l*(1<<7));
    for(ll i=128;i<238;i++){
        cout<<i<<" ";
    }
    f();
    ll b;
    cin>>b;
    b&=((1<<7)-1);
    a^=b;
    cout<<a<<endl;

}


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define vd(T) vector<vector<T>>
#define v(T) vector<T>
#define mod (ll)(1e9+7)
#define pp(T) pair<T,T>
#define pb push_back
#define read1(x) scanf("%d",&x)
#define read2(x,y) scanf("%d%d",&x,&y)
#define read3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define read4(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
#define mp make_pair
#define NN 100005
#define IO ios_base::sync_with_stdio(NULL),cin.tie(0),cout.tie(0)
#define loop(i,a,b) for(ll i=a;i<b;i++)
#define DEBUG cout<<"***********DEBUGGING***********"<<endl
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
typedef tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update> ordered_set;
ll power(ll a,ll b){ if(b==0) return 1; ll s=power(a,b/2); s*=s;if(b&1) return s*a; return s;}
ll powermod(ll a,ll b,ll m){ if(b==0) return 1; ll s=power(a,b/2);s*=s; s%=m; if(b&1) return (s*a)%m; return s;}
inline int add(ll a,ll b,ll m=0){
    if(m==0) return a+b;
    return (a%m+b%m)%m;
}

inline int sub(ll a,ll b ,ll m=0){
    if(m==0) return a-b;
    return ((a%m-b%m)%m+m)%m;
}

inline int mul(ll a,ll b,ll m=0){
    if(m==0)return (a*b);
    return ((a%m)*(b%m))%m;
}

inline int div(ll a,ll b,ll m=0){
    if(m==0) return a/b;
    return ((a%m)*(powermod(a,b,m-2)%m))%m;
}

bool isopen(char c){
    return ((c=='(')||(c=='{')||(c=='['));
}

ll longest_balanced_parenthesis(string a,ll n){
    stack<char> s;
    ll start=0;
    ll ans=0,m=0;
    for(ll i=0;i<n;i++){
        if(isopen(a[i])){
            s.push(a[i]);
        }
        else{
            if(isopen(a[i])|| s.empty()){
                m=max(m,ans);
                continue;
            }
            else{
                s.pop();
                ans+=2;
                m=max(ans,m);
            }
        }
    }

    return m;

}

ll setup_k(ll *a,ll k){
  ordered_set x;
  // Now , insert and then check number of integers less than k by just using x.order_of_key(k+1)
  return 0;
}

void f(){
    fflush(stdout);
}

int main() {

    IO;

    ll a=0;
    cout<<"?"<< " ";
    for(ll i=0;i<100;i++){
        cout<<i<<" ";
    }
    cout<<endl;
    f();
    cin>>a;
    cout<<"?"<< " ";
    ll l=(1<<7)-1;
    a&=(l*(1<<7));
    for(ll i=128;i<238;i++){
        cout<<i<<" ";
    }
    cout<<endl;
    f();
    ll b;
    cin>>b;
    b&=((1<<7)-1);
    a^=b;
    cout<<"! ";
    cout<<a<<endl;

}


#include<bits/stdc++.h>
using namespace std;
map<int,int> m;
int a,b;

int main(){
cout<<"? ";
for(int i=1;i<=100;i++)
cout<<i<<" ";cout<<endl;
cin>>a;

cout<<"? ";

for(int i=1;i<=100;i++)
cout<<(i<<7)<<" ";cout<<endl;
cin>>b;

int ans= (a>>7)<<7;

ans += ((1<<7)-1)&b;

cout<<"! "<<ans<<endl;
}


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define vd(T) vector<vector<T>>
#define v(T) vector<T>
#define mod (ll)(1e9+7)
#define pp(T) pair<T,T>
#define pb push_back
#define read1(x) scanf("%d",&x)
#define read2(x,y) scanf("%d%d",&x,&y)
#define read3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define read4(a,b,c,d) scanf("%d%d%d%d",&a,&b,&c,&d)
#define mp make_pair
#define NN 100005
#define IO ios_base::sync_with_stdio(NULL),cin.tie(0),cout.tie(0)
#define loop(i,a,b) for(ll i=a;i<b;i++)
#define DEBUG cout<<"***********DEBUGGING***********"<<endl
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
typedef tree<ll, null_type, less<ll>, rb_tree_tag, tree_order_statistics_node_update> ordered_set;
ll power(ll a,ll b){ if(b==0) return 1; ll s=power(a,b/2); s*=s;if(b&1) return s*a; return s;}
ll powermod(ll a,ll b,ll m){ if(b==0) return 1; ll s=power(a,b/2);s*=s; s%=m; if(b&1) return (s*a)%m; return s;}
inline int add(ll a,ll b,ll m=0){
    if(m==0) return a+b;
    return (a%m+b%m)%m;
}

inline int sub(ll a,ll b ,ll m=0){
    if(m==0) return a-b;
    return ((a%m-b%m)%m+m)%m;
}

inline int mul(ll a,ll b,ll m=0){
    if(m==0)return (a*b);
    return ((a%m)*(b%m))%m;
}

inline int div(ll a,ll b,ll m=0){
    if(m==0) return a/b;
    return ((a%m)*(powermod(a,b,m-2)%m))%m;
}

bool isopen(char c){
    return ((c=='(')||(c=='{')||(c=='['));
}

ll longest_balanced_parenthesis(string a,ll n){
    stack<char> s;
    ll start=0;
    ll ans=0,m=0;
    for(ll i=0;i<n;i++){
        if(isopen(a[i])){
            s.push(a[i]);
        }
        else{
            if(isopen(a[i])|| s.empty()){
                m=max(m,ans);
                continue;
            }
            else{
                s.pop();
                ans+=2;
                m=max(ans,m);
            }
        }
    }

    return m;

}

ll setup_k(ll *a,ll k){
  ordered_set x;
  // Now , insert and then check number of integers less than k by just using x.order_of_key(k+1)
  return 0;
}

void f(){
    fflush(stdout);
}

int main() {

    IO;

    ll a=0;
    cout<<"?"<< " ";
    for(ll i=0;i<100;i++){
        cout<<i<<" ";
    }
    f();
    cin>>a;
    cout<<"?"<< " ";
    ll l=(1<<7)-1;
    a&=(l*(1<<7));
    for(ll i=128;i<238;i++){
        cout<<i<<" ";
    }
    f();
    ll b;
    cin>>b;
    b&=((1<<7)-1);
    a^=b;
    cout<<"! ";
    cout<<a<<endl;

}


#include<iostream>
#include<random>
#include<ctime>
#include<vector>
#include<algorithm>

using namespace std;

vector<int> a;

int main()
{
    for (int i=0; i<100; i++)
    {
        a.push_back(i);
    }
    for (int i=1; i<=100; i++)
    {
        a.push_back(i*128);
    }
    int k1,k2;
    cout<<"? ";
    for (int i=0; i<100; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    cin>>k1;
    cout<<"? ";
    for (int i=100; i<200; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    cin>>k2;
    cout<<"! "<<((k1>>7)<<7)+(k2&(127));
}


#include<iostream>
#include<random>
#include<ctime>
#include<vector>
#include<algorithm>

using namespace std;

mt19937 rnd(time(NULL));

vector<int> a;

int p[14]={2,4,8,26,44,62,80,98,116,134,152,170,188,200};

bool used[1000000+5];

void gener()
{
    for (int i=0; i<100; i++)
    {
        while (true)
        {
            int l=rnd()%(1<<14);
            while (used[l]) l=rnd()%(1<<14);
            bool can=true;
            for (int j=0; j<(int)a.size(); j++)
            {
                if (a[j]==l)
                {
                    can=false;
                    break;
                }
            }
            if (can)
            {
                a.push_back(l);
                break;
            }
        }
    }
}

int main()
{
    used[0]=true;
    for (int i=0; i<14; i++)
    {
        used[(1<<i)]=true;
    }
    gener();
    //cout<<"Ready!"<<'\n';
    /*int n;
    cin>>n;*/
    int n;
    int n_ans=0;
    while (true)
    {
        if (n_ans>4) break;
        n=rnd()%(1<<14);
        gener();
        for (int j=0; j<15; j++)
        {
            a.pop_back();
        }
        for (int j=0; j<14; j++)
        {
            a.push_back(1<<j);
        }
        a.push_back(0);
        int l1=rnd()%100;
        int l2=rnd()%100;
        int r1=(a[l1]^n);
        int r2=(a[l2+100]^n);
        int cnt=0;
        int mt=0;
        for (int i=0; i<100; i++)
        {
            for (int j=100; j<200; j++)
            {
                for (int k=0; k<(1<<14); k++)
                {
                    if ((a[i]^k)==r1&&(a[j]^k)==r2)
                    {
                        //cout<<a[i]<<" "<<a[j]<<" "<<k<<'\n';
                        cnt++;
                        mt=k;
                    }
                }
            }
        }
        if (cnt==1)
        {
            n_ans++;
        }
        for (int i=0; i<100; i++)
        {
            a.pop_back();
        }
    }
    /*int N;
    cin>>N;*/
    cout<<"? ";
    for (int i=0; i<100; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k1;
    cin>>k1;
    //k1=a[rnd()%100]^N;
    cout<<"? ";
    for (int i=100; i<200; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k2;
    cin>>k2;
    //k2=a[rnd()%100+100]^N;
    vector<int> guesses;
    for (int i=0; i<100; i++)
    {
        for (int j=100; j<200; j++)
        {
            for (int k=0; k<(1<<14); k++)
            {
                if ((a[i]^k)==k1&&(a[j]^k)==k2)
                {
                    /*cout<<"! "<<k<<endl;
                    return 0;*/
                    guesses.push_back(k);
                }
            }
        }
    }
    sort(guesses.begin(), guesses.end());
    cout<<"! "<<guesses[0]<<endl;
}


#include<iostream>
#include<random>
#include<ctime>
#include<vector>

using namespace std;

mt19937 rnd(time(NULL));

vector<int> a;

int p[14]={2,4,8,26,44,62,80,98,116,134,152,170,188,200};

bool used[1000000+5];

void gener()
{
    for (int i=0; i<100; i++)
    {
        while (true)
        {
            int l=rnd()%(1<<14);
            while (used[l]) l=rnd()%(1<<14);
            bool can=true;
            for (int j=0; j<(int)a.size(); j++)
            {
                if (a[j]==l)
                {
                    can=false;
                    break;
                }
            }
            if (can)
            {
                a.push_back(l);
                break;
            }
        }
    }
}

int main()
{
    used[0]=true;
    for (int i=0; i<14; i++)
    {
        used[(1<<i)]=true;
    }
    gener();
    //cout<<"Ready!"<<'\n';
    /*int n;
    cin>>n;*/
    int n;
    int n_ans=0;
    bool need=true;
    while (true)
    {
        if (n_ans>3) break;
        if (need)
        {
            n=rnd()%(1<<14);
            gener();
            for (int j=0; j<15; j++)
            {
                a.pop_back();
            }
            for (int j=0; j<14; j++)
            {
                a.push_back(1<<j);
            }
            a.push_back(0);
        }
        need=true;
        int l1=rnd()%100;
        int l2=rnd()%100;
        int r1=(a[l1]^n);
        int r2=(a[l2+100]^n);
        int cnt=0;
        int mt=0;
        for (int i=0; i<100; i++)
        {
            if (cnt>1) break;
            for (int j=100; j<200; j++)
            {
                if (cnt>1) break;
                for (int k=0; k<(1<<14); k++)
                {
                    if (cnt>1) break;
                    if ((a[i]^k)==r1&&(a[j]^k)==r2)
                    {
                        //cout<<a[i]<<" "<<a[j]<<" "<<k<<'\n';
                        cnt++;
                        mt=k;
                    }
                }
            }
        }
        if (cnt==1)
        {
            n_ans++;
            need=false;
        }
        else
        {
            for (int i=0; i<100; i++)
            {
                a.pop_back();
            }
        }
    }
    /*int N;
    cin>>N;*/
    cout<<"? ";
    for (int i=0; i<100; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k1;
    cin>>k1;
    //k1=a[rnd()%100]^N;
    cout<<"? ";
    for (int i=100; i<200; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k2;
    cin>>k2;
    //k2=a[rnd()%100+100]^N;
    vector<int> guesses;
    for (int i=0; i<100; i++)
    {
        for (int j=100; j<200; j++)
        {
            for (int k=0; k<(1<<14); k++)
            {
                if ((a[i]^k)==k1&&(a[j]^k)==k2)
                {
                    guesses.push_back(k);
                }
            }
        }
    }
    for (auto i:guesses)
    {
        if (i==0)
        {
            cout<<"! 0"<<endl;
            return 0;
        }
        if (i==16383)
        {
            cout<<"! 16383"<<endl;
            return 0;
        }
    }
    cout<<"! "<<guesses[0]<<endl;
    return 0;
}


#include<iostream>
#include<random>
#include<ctime>
#include<vector>
#include<algorithm>

using namespace std;

mt19937 rnd(time(NULL));

vector<int> a;

int p[14]={2,4,8,26,44,62,80,98,116,134,152,170,188,200};

bool used[1000000+5];

void gener()
{
    for (int i=0; i<100; i++)
    {
        while (true)
        {
            int l=rnd()%(1<<14);
            while (used[l]) l=rnd()%(1<<14);
            bool can=true;
            for (int j=0; j<(int)a.size(); j++)
            {
                if (a[j]==l)
                {
                    can=false;
                    break;
                }
            }
            if (can)
            {
                a.push_back(l);
                break;
            }
        }
    }
}

int main()
{
    used[0]=true;
    for (int i=0; i<14; i++)
    {
        used[(1<<i)]=true;
    }
    gener();
    //cout<<"Ready!"<<'\n';
    /*int n;
    cin>>n;*/
    int n;
    int n_ans=0;
    while (true)
    {
        if (n_ans>3) break;
        n=rnd()%(1<<14);
        gener();
        for (int j=0; j<15; j++)
        {
            a.pop_back();
        }
        for (int j=0; j<14; j++)
        {
            a.push_back(1<<j);
        }
        a.push_back(0);
        int l1=rnd()%100;
        int l2=rnd()%100;
        int r1=(a[l1]^n);
        int r2=(a[l2+100]^n);
        int cnt=0;
        int mt=0;
        for (int i=0; i<100; i++)
        {
            for (int j=100; j<200; j++)
            {
                for (int k=0; k<(1<<14); k++)
                {
                    if ((a[i]^k)==r1&&(a[j]^k)==r2)
                    {
                        //cout<<a[i]<<" "<<a[j]<<" "<<k<<'\n';
                        cnt++;
                        mt=k;
                    }
                }
            }
        }
        if (cnt==1)
        {
            n_ans++;
        }
        for (int i=0; i<100; i++)
        {
            a.pop_back();
        }
    }
    for (int i=0; i<2000; i++)
    {
        swap(a[rnd()%200],a[rnd()%200]);
    }
    /*int N;
    cin>>N;*/
    cout<<"? ";
    for (int i=0; i<100; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k1;
    cin>>k1;
    //k1=a[rnd()%100]^N;
    cout<<"? ";
    for (int i=100; i<200; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k2;
    cin>>k2;
    //k2=a[rnd()%100+100]^N;
    vector<int> guesses;
    for (int i=0; i<100; i++)
    {
        for (int j=100; j<200; j++)
        {
            for (int k=0; k<(1<<14); k++)
            {
                if ((a[i]^k)==k1&&(a[j]^k)==k2)
                {
                    /*cout<<"! "<<k<<endl;
                    return 0;*/
                    guesses.push_back(k);
                }
            }
        }
    }
    sort(guesses.begin(), guesses.end());
    cout<<"! "<<guesses[0]<<endl;
}


#include<iostream>
#include<random>
#include<ctime>
#include<vector>

using namespace std;

mt19937 rnd(time(NULL));

vector<int> a;

int p[14]={2,4,8,26,44,62,80,98,116,134,152,170,188,200};

bool used[1000000+5];

void gener()
{
    for (int i=0; i<100; i++)
    {
        while (true)
        {
            int l=rnd()%(1<<14);
            while (used[l]) l=rnd()%(1<<14);
            bool can=true;
            for (int j=0; j<(int)a.size(); j++)
            {
                if (a[j]==l)
                {
                    can=false;
                    break;
                }
            }
            if (can)
            {
                a.push_back(l);
                break;
            }
        }
    }
}

int main()
{
    used[0]=true;
    for (int i=0; i<14; i++)
    {
        used[(1<<i)]=true;
    }
    gener();
    //cout<<"Ready!"<<'\n';
    /*int n;
    cin>>n;*/
    int n;
    int n_ans=0;
    while (true)
    {
        if (n_ans>3) break;
        n=rnd()%(1<<14);
        gener();
        for (int j=0; j<15; j++)
        {
            a.pop_back();
        }
        for (int j=0; j<14; j++)
        {
            a.push_back(1<<j);
        }
        a.push_back(0);
        int l1=rnd()%100;
        int l2=rnd()%100;
        int r1=(a[l1]^n);
        int r2=(a[l2+100]^n);
        int cnt=0;
        int mt=0;
        for (int i=0; i<100; i++)
        {
            for (int j=100; j<200; j++)
            {
                for (int k=0; k<(1<<14); k++)
                {
                    if ((a[i]^k)==r1&&(a[j]^k)==r2)
                    {
                        //cout<<a[i]<<" "<<a[j]<<" "<<k<<'\n';
                        cnt++;
                        mt=k;
                    }
                }
            }
        }
        if (cnt==1)
        {
            n_ans++;
            continue;
        }
        for (int i=0; i<100; i++)
        {
            a.pop_back();
        }
    }
    /*int N;
    cin>>N;*/
    cout<<"? ";
    for (int i=0; i<100; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k1;
    cin>>k1;
    //k1=a[rnd()%100]^N;
    cout<<"? ";
    for (int i=100; i<200; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k2;
    cin>>k2;
    //k2=a[rnd()%100+100]^N;
    vector<int> guesses;
    for (int i=0; i<100; i++)
    {
        for (int j=100; j<200; j++)
        {
            for (int k=0; k<(1<<14); k++)
            {
                if ((a[i]^k)==k1&&(a[j]^k)==k2)
                {
                    guesses.push_back(k);
                }
            }
        }
    }
    for (auto i:guesses)
    {
        if (i==0)
        {
            cout<<"! 0"<<endl;
            return 0;
        }
        if (i==16383)
        {
            cout<<"! 16383"<<endl;
            return 0;
        }
    }
    cout<<"! "<<guesses[0]<<endl;
    return 0;
}


#include<iostream>
#include<random>
#include<ctime>
#include<vector>

using namespace std;

mt19937 rnd(time(NULL));

vector<int> a;

int p[14]={2,4,8,26,44,62,80,98,116,134,152,170,188,200};

bool used[1000000+5];

void gener()
{
    for (int i=0; i<100; i++)
    {
        while (true)
        {
            int l=rnd()%(1<<14);
            while (used[l]) l=rnd()%(1<<14);
            bool can=true;
            for (int j=0; j<(int)a.size(); j++)
            {
                if (a[j]==l)
                {
                    can=false;
                    break;
                }
            }
            if (can)
            {
                a.push_back(l);
                break;
            }
        }
    }
}

int main()
{
    used[0]=true;
    for (int i=0; i<14; i++)
    {
        used[(1<<i)]=true;
    }
    gener();
    //cout<<"Ready!"<<'\n';
    /*int n;
    cin>>n;*/
    int n;
    int n_ans=0;
    bool need=true;
    while (true)
    {
        if (n_ans>2) break;
        if (need)
        {
            n=rnd()%(1<<14);
            gener();
            for (int j=0; j<15; j++)
            {
                a.pop_back();
            }
            for (int j=0; j<14; j++)
            {
                a.push_back(1<<j);
            }
            a.push_back(0);
        }
        need=true;
        int l1=rnd()%100;
        int l2=rnd()%100;
        int r1=(a[l1]^n);
        int r2=(a[l2+100]^n);
        int cnt=0;
        int mt=0;
        for (int i=0; i<100; i++)
        {
            for (int j=100; j<200; j++)
            {
                for (int k=0; k<(1<<14); k++)
                {
                    if ((a[i]^k)==r1&&(a[j]^k)==r2)
                    {
                        //cout<<a[i]<<" "<<a[j]<<" "<<k<<'\n';
                        cnt++;
                        mt=k;
                    }
                }
            }
        }
        if (cnt==1)
        {
            n_ans++;
            need=false;
        }
        else
        {
            for (int i=0; i<100; i++)
            {
                a.pop_back();
            }
        }
    }
    /*int N;
    cin>>N;*/
    cout<<"? ";
    for (int i=0; i<100; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k1;
    cin>>k1;
    //k1=a[rnd()%100]^N;
    cout<<"? ";
    for (int i=100; i<200; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k2;
    cin>>k2;
    //k2=a[rnd()%100+100]^N;
    vector<int> guesses;
    for (int i=0; i<100; i++)
    {
        for (int j=100; j<200; j++)
        {
            for (int k=0; k<(1<<14); k++)
            {
                if ((a[i]^k)==k1&&(a[j]^k)==k2)
                {
                    guesses.push_back(k);
                }
            }
        }
    }
    for (auto i:guesses)
    {
        if (i==0)
        {
            cout<<"! 0"<<endl;
            return 0;
        }
        if (i==16383)
        {
            cout<<"! 16383"<<endl;
            return 0;
        }
    }
    cout<<"! "<<guesses[0]<<endl;
    return 0;
}


#include<iostream>
#include<random>
#include<ctime>
#include<vector>

using namespace std;

mt19937 rnd(time(NULL));

vector<int> a;

int p[14]={2,4,8,26,44,62,80,98,116,134,152,170,188,200};

bool used[1000000+5];

void gener()
{
    for (int i=0; i<100; i++)
    {
        while (true)
        {
            int l=rnd()%(1<<14);
            while (used[l]) l=rnd()%(1<<14);
            bool can=true;
            for (int j=0; j<(int)a.size(); j++)
            {
                if (a[j]==l)
                {
                    can=false;
                    break;
                }
            }
            if (can)
            {
                a.push_back(l);
                break;
            }
        }
    }
}

int main()
{
    used[0]=true;
    for (int i=0; i<14; i++)
    {
        used[(1<<i)]=true;
    }
    gener();
    //cout<<"Ready!"<<'\n';
    /*int n;
    cin>>n;*/
    int n;
    int n_ans=0;
    while (true)
    {
        if (n_ans>3) break;
        n=rnd()%(1<<14);
        gener();
        for (int j=0; j<15; j++)
        {
            a.pop_back();
        }
        for (int j=0; j<14; j++)
        {
            a.push_back(1<<j);
        }
        a.push_back(0);
        int l1=rnd()%100;
        int l2=rnd()%100;
        int r1=(a[l1]^n);
        int r2=(a[l2+100]^n);
        int cnt=0;
        int mt=0;
        for (int i=0; i<100; i++)
        {
            for (int j=100; j<200; j++)
            {
                for (int k=0; k<(1<<14); k++)
                {
                    if ((a[i]^k)==r1&&(a[j]^k)==r2)
                    {
                        //cout<<a[i]<<" "<<a[j]<<" "<<k<<'\n';
                        cnt++;
                        mt=k;
                    }
                }
            }
        }
        if (cnt==1)
        {
            n_ans++;
            continue;
        }
        for (int i=0; i<100; i++)
        {
            a.pop_back();
        }
    }
    /*int N;
    cin>>N;*/
    cout<<"? ";
    for (int i=0; i<100; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k1;
    cin>>k1;
    //k1=a[rnd()%100]^N;
    cout<<"? ";
    for (int i=100; i<200; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k2;
    cin>>k2;
    //k2=a[rnd()%100+100]^N;
    vector<int> guesses;
    for (int i=0; i<100; i++)
    {
        for (int j=100; j<200; j++)
        {
            for (int k=0; k<(1<<14); k++)
            {
                if ((a[i]^k)==k1&&(a[j]^k)==k2)
                {
                    guesses.push_back(k);
                }
            }
        }
    }
    for (auto i:guesses)
    {
        if (i==0)
        {
            cout<<"! 0"<<endl;
            return 0;
        }
    }
    cout<<"! "<<guesses[0]<<endl;
    return 0;
}


#include<iostream>
#include<random>
#include<ctime>
#include<vector>
#include<algorithm>

using namespace std;

mt19937 rnd(time(NULL));

vector<int> a;

int p[14]={2,4,8,26,44,62,80,98,116,134,152,170,188,200};

bool used[1000000+5];

void gener()
{
    for (int i=0; i<100; i++)
    {
        while (true)
        {
            int l=rnd()%(1<<14);
            while (used[l]) l=rnd()%(1<<14);
            bool can=true;
            for (int j=0; j<(int)a.size(); j++)
            {
                if (a[j]==l)
                {
                    can=false;
                    break;
                }
            }
            if (can)
            {
                a.push_back(l);
                break;
            }
        }
    }
}

int main()
{
    used[0]=true;
    gener();
    //cout<<"Ready!"<<'\n';
    /*int n;
    cin>>n;*/
    int n;
    int n_ans=0;
    while (true)
    {
        if (n_ans>3+rnd()%2) break;
        n=rnd()%(1<<14);
        gener();
        for (int j=0; j<1; j++)
        {
            a.pop_back();
        }
        a.push_back(0);
        int l1=rnd()%100;
        int l2=rnd()%100;
        int r1=(a[l1]^n);
        int r2=(a[l2+100]^n);
        int cnt=0;
        int mt=0;
        for (int i=0; i<100; i++)
        {
            if (cnt>1) break;
            for (int j=100; j<200; j++)
            {
                if (cnt>1) break;
                for (int k=0; k<(1<<14); k++)
                {
                    if ((a[i]^k)==r1&&(a[j]^k)==r2)
                    {
                        //cout<<a[i]<<" "<<a[j]<<" "<<k<<'\n';
                        cnt++;
                        mt=k;
                        if (cnt>1) break;
                    }
                }
            }
        }
        if (cnt==1)
        {
            n_ans++;
        }
        for (int i=0; i<100; i++)
        {
            a.pop_back();
        }
    }
    /*int N;
    cin>>N;*/
    cout<<"? ";
    for (int i=0; i<100; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k1;
    cin>>k1;
    //k1=a[rnd()%100]^N;
    cout<<"? ";
    for (int i=100; i<200; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k2;
    cin>>k2;
    //k2=a[rnd()%100+100]^N;
    vector<int> guesses;
    for (int i=0; i<100; i++)
    {
        for (int j=100; j<200; j++)
        {
            for (int k=0; k<(1<<14); k++)
            {
                if ((a[i]^k)==k1&&(a[j]^k)==k2)
                {
                    /*cout<<"! "<<k<<endl;
                    return 0;*/
                    guesses.push_back(k);
                }
            }
        }
    }
    sort(guesses.begin(), guesses.end());
    cout<<"! "<<guesses[0]<<endl;
}


#include<iostream>
#include<random>
#include<ctime>
#include<vector>

using namespace std;

mt19937 rnd(time(NULL));

vector<int> a;

int p[14]={2,4,8,26,44,62,80,98,116,134,152,170,188,200};

bool used[1000000+5];

void gener()
{
    for (int i=0; i<100; i++)
    {
        while (true)
        {
            int l=rnd()%(1<<14);
            while (used[l]) l=rnd()%(1<<14);
            bool can=true;
            for (int j=0; j<(int)a.size(); j++)
            {
                if (a[j]==l)
                {
                    can=false;
                    break;
                }
            }
            if (can)
            {
                a.push_back(l);
                break;
            }
        }
    }
}

int main()
{
    used[0]=true;
    for (int i=0; i<14; i++)
    {
        used[(1<<i)]=true;
    }
    gener();
    //cout<<"Ready!"<<'\n';
    /*int n;
    cin>>n;*/
    int n;
    int n_ans=0;
    bool need=true;
    while (true)
    {
        if (n_ans>3) break;
        if (need)
        {
            n=rnd()%(1<<14);
            gener();
            for (int j=0; j<15; j++)
            {
                a.pop_back();
            }
            for (int j=0; j<14; j++)
            {
                a.push_back(1<<j);
            }
            a.push_back(0);
        }
        need=true;
        int l1=rnd()%100;
        int l2=rnd()%100;
        int r1=(a[l1]^n);
        int r2=(a[l2+100]^n);
        int cnt=0;
        int mt=0;
        for (int i=0; i<100; i++)
        {
            if (cnt>1) break;
            for (int j=100; j<200; j++)
            {
                if (cnt>1) break;
                for (int k=0; k<(1<<14); k++)
                {
                    if (cnt>1) break;
                    if ((a[i]^k)==r1&&(a[j]^k)==r2)
                    {
                        //cout<<a[i]<<" "<<a[j]<<" "<<k<<'\n';
                        cnt++;
                        mt=k;
                    }
                }
            }
        }
        if (cnt==1)
        {
            n_ans++;
            need=false;
        }
        else
        {
            for (int i=0; i<100; i++)
            {
                a.pop_back();
            }
        }
    }
    /*int N;
    cin>>N;*/
    cout<<"? ";
    for (int i=0; i<100; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k1;
    cin>>k1;
    //k1=a[rnd()%100]^N;
    cout<<"? ";
    for (int i=100; i<200; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k2;
    cin>>k2;
    //k2=a[rnd()%100+100]^N;
    vector<int> guesses;
    for (int i=0; i<100; i++)
    {
        for (int j=100; j<200; j++)
        {
            for (int k=0; k<(1<<14); k++)
            {
                if ((a[i]^k)==k1&&(a[j]^k)==k2)
                {
                    guesses.push_back(k);
                }
            }
        }
    }
    for (auto i:guesses)
    {
        if (used[i])
        {
            cout<<"! "<<i<<endl;
            return 0;
        }
        if (i==16383)
        {
            cout<<"! 16383"<<endl;
            return 0;
        }
    }
    cout<<"! "<<guesses[0]<<endl;
    return 0;
}


#include<iostream>
#include<random>
#include<ctime>
#include<vector>
#include<algorithm>

using namespace std;

vector<int> a;

int main()
{
    for (int i=0; i<100; i++)
    {
        a.push_back(i);
    }
    for (int i=1; i<=100; i++)
    {
        a.push_back(i*128);
    }
    int k1,k2;
    cout<<"? ";
    for (int i=0; i<100; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    cin>>k1;
    cout<<"? ";
    for (int i=100; i<200; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    cin>>k2;
    cout<<((k1>>7)<<7)+(k2&(127));
}


#include<iostream>
#include<random>
#include<ctime>
#include<vector>
#include<algorithm>

using namespace std;

mt19937 rnd(time(NULL));

vector<int> a;

int p[14]={2,4,8,26,44,62,80,98,116,134,152,170,188,200};

bool used[1000000+5];

void gener()
{
    for (int i=0; i<100; i++)
    {
        while (true)
        {
            int l=rnd()%(1<<14);
            while (used[l]) l=rnd()%(1<<14);
            bool can=true;
            for (int j=0; j<(int)a.size(); j++)
            {
                if (a[j]==l)
                {
                    can=false;
                    break;
                }
            }
            if (can)
            {
                a.push_back(l);
                break;
            }
        }
    }
}

int main()
{
    used[0]=true;
    for (int i=0; i<14; i++)
    {
        used[(1<<i)]=true;
    }
    gener();
    //cout<<"Ready!"<<'\n';
    /*int n;
    cin>>n;*/
    int n;
    int n_ans=0;
    while (true)
    {
        if (n_ans>3+rnd()%2) break;
        n=rnd()%(1<<14);
        gener();
        for (int j=0; j<15; j++)
        {
            a.pop_back();
        }
        for (int j=0; j<14; j++)
        {
            a.push_back(1<<j);
        }
        a.push_back(0);
        int l1=rnd()%100;
        int l2=rnd()%100;
        int r1=(a[l1]^n);
        int r2=(a[l2+100]^n);
        int cnt=0;
        int mt=0;
        for (int i=0; i<100; i++)
        {
            if (cnt>1) break;
            for (int j=100; j<200; j++)
            {
                if (cnt>1) break;
                for (int k=0; k<(1<<14); k++)
                {
                    if ((a[i]^k)==r1&&(a[j]^k)==r2)
                    {
                        //cout<<a[i]<<" "<<a[j]<<" "<<k<<'\n';
                        cnt++;
                        mt=k;
                        if (cnt>1) break;
                    }
                }
            }
        }
        if (cnt==1)
        {
            n_ans++;
        }
        for (int i=0; i<100; i++)
        {
            a.pop_back();
        }
    }
    /*int N;
    cin>>N;*/
    cout<<"? ";
    for (int i=0; i<100; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k1;
    cin>>k1;
    //k1=a[rnd()%100]^N;
    cout<<"? ";
    for (int i=100; i<200; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k2;
    cin>>k2;
    //k2=a[rnd()%100+100]^N;
    vector<int> guesses;
    for (int i=0; i<100; i++)
    {
        for (int j=100; j<200; j++)
        {
            for (int k=0; k<(1<<14); k++)
            {
                if ((a[i]^k)==k1&&(a[j]^k)==k2)
                {
                    /*cout<<"! "<<k<<endl;
                    return 0;*/
                    guesses.push_back(k);
                }
            }
        }
    }
    sort(guesses.begin(), guesses.end());
    cout<<"! "<<guesses[0]<<endl;
}


#include<iostream>
#include<random>
#include<ctime>
#include<vector>
#include<algorithm>

using namespace std;

mt19937 rnd(time(NULL));

vector<int> a;

int p[14]={2,4,8,26,44,62,80,98,116,134,152,170,188,200};

bool used[1000000+5];

void gener()
{
    for (int i=0; i<100; i++)
    {
        while (true)
        {
            int l=rnd()%(1<<14);
            while (used[l]) l=rnd()%(1<<14);
            bool can=true;
            for (int j=0; j<(int)a.size(); j++)
            {
                if (a[j]==l)
                {
                    can=false;
                    break;
                }
            }
            if (can)
            {
                a.push_back(l);
                break;
            }
        }
    }
}

int main()
{
    used[0]=true;
    for (int i=0; i<14; i++)
    {
        used[(1<<i)]=true;
    }
    gener();
    //cout<<"Ready!"<<'\n';
    /*int n;
    cin>>n;*/
    int n;
    int n_ans=0;
    while (true)
    {
        if (n_ans>3+rnd()%3) break;
        n=rnd()%(1<<14);
        gener();
        for (int j=0; j<15; j++)
        {
            a.pop_back();
        }
        for (int j=0; j<14; j++)
        {
            a.push_back(1<<j);
        }
        a.push_back(0);
        int l1=rnd()%100;
        int l2=rnd()%100;
        int r1=(a[l1]^n);
        int r2=(a[l2+100]^n);
        int cnt=0;
        int mt=0;
        for (int i=0; i<100; i++)
        {
            for (int j=100; j<200; j++)
            {
                for (int k=0; k<(1<<14); k++)
                {
                    if ((a[i]^k)==r1&&(a[j]^k)==r2)
                    {
                        //cout<<a[i]<<" "<<a[j]<<" "<<k<<'\n';
                        cnt++;
                        mt=k;
                    }
                }
            }
        }
        if (cnt==1)
        {
            n_ans++;
        }
        for (int i=0; i<100; i++)
        {
            a.pop_back();
        }
    }
    /*int N;
    cin>>N;*/
    cout<<"? ";
    for (int i=0; i<100; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k1;
    cin>>k1;
    //k1=a[rnd()%100]^N;
    cout<<"? ";
    for (int i=100; i<200; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k2;
    cin>>k2;
    //k2=a[rnd()%100+100]^N;
    vector<int> guesses;
    for (int i=0; i<100; i++)
    {
        for (int j=100; j<200; j++)
        {
            for (int k=0; k<(1<<14); k++)
            {
                if ((a[i]^k)==k1&&(a[j]^k)==k2)
                {
                    /*cout<<"! "<<k<<endl;
                    return 0;*/
                    guesses.push_back(k);
                }
            }
        }
    }
    sort(guesses.begin(), guesses.end());
    cout<<"! "<<guesses[0]<<endl;
}


#include<iostream>
#include<random>
#include<ctime>
#include<vector>

using namespace std;

mt19937 rnd(time(NULL));

vector<int> a;

int p[14]={2,4,8,26,44,62,80,98,116,134,152,170,188,200};

bool used[1000000+5];

void gener()
{
    for (int i=0; i<100; i++)
    {
        while (true)
        {
            int l=rnd()%(1<<14);
            while (used[l]) l=rnd()%(1<<14);
            bool can=true;
            for (int j=0; j<(int)a.size(); j++)
            {
                if (a[j]==l)
                {
                    can=false;
                    break;
                }
            }
            if (can)
            {
                a.push_back(l);
                break;
            }
        }
    }
}

int main()
{
    used[0]=true;
    for (int i=0; i<14; i++)
    {
        used[(1<<i)]=true;
    }
    gener();
    //cout<<"Ready!"<<'\n';
    /*int n;
    cin>>n;*/
    int n;
    int n_ans=0;
    bool need=true;
    int pihcnt=0;
    while (true)
    {
        if (n_ans>3) break;
        pihcnt++;
        if (pihcnt==10) break;
        if (need)
        {
            n=rnd()%(1<<14);
            gener();
            for (int j=0; j<15; j++)
            {
                a.pop_back();
            }
            for (int j=0; j<14; j++)
            {
                a.push_back(1<<j);
            }
            a.push_back(0);
        }
        need=true;
        int l1=rnd()%100;
        int l2=rnd()%100;
        int r1=(a[l1]^n);
        int r2=(a[l2+100]^n);
        int cnt=0;
        int mt=0;
        for (int i=0; i<100; i++)
        {
            if (cnt>1) break;
            for (int j=100; j<200; j++)
            {
                if (cnt>1) break;
                for (int k=0; k<(1<<14); k++)
                {
                    if (cnt>1) break;
                    if ((a[i]^k)==r1&&(a[j]^k)==r2)
                    {
                        //cout<<a[i]<<" "<<a[j]<<" "<<k<<'\n';
                        cnt++;
                        mt=k;
                    }
                }
            }
        }
        if (cnt==1)
        {
            n_ans++;
            need=false;
        }
        else
        {
            for (int i=0; i<100; i++)
            {
                a.pop_back();
            }
        }
    }
    /*int N;
    cin>>N;*/
    cout<<"? ";
    for (int i=0; i<100; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k1;
    cin>>k1;
    //k1=a[rnd()%100]^N;
    cout<<"? ";
    for (int i=100; i<200; i++)
    {
        cout<<a[i]<<" ";
    }
    cout<<endl;
    int k2;
    cin>>k2;
    //k2=a[rnd()%100+100]^N;
    vector<int> guesses;
    for (int i=0; i<100; i++)
    {
        for (int j=100; j<200; j++)
        {
            for (int k=0; k<(1<<14); k++)
            {
                if ((a[i]^k)==k1&&(a[j]^k)==k2)
                {
                    guesses.push_back(k);
                }
            }
        }
    }
    for (auto i:guesses)
    {
        if (i==0)
        {
            cout<<"! 0"<<endl;
            return 0;
        }
        if (i==16383)
        {
            cout<<"! 16383"<<endl;
            return 0;
        }
    }
    cout<<"! "<<guesses[0]<<endl;
    return 0;
}


#include <bits/stdc++.h>
using namespace std;
int n,m,q,a[202020],b[202020],r,c,ans;

int main(){
	cin>>n>>m>>q;
	while(q--){
		cin>>r>>c;
		a[r] = 1;
		b[c] = 1;
	}
	for(int i = 1;i<=n;i++)
		if(!a[i]) ans++;
	for(int i = 1;i<=m;i++)
		if(!b[i]) ans++;
	cout<<ans;
 	return 0;
}


#include <bits/stdc++.h>

using namespace std;
const int N = 200020;
int n,m,q,u,v,fa[N*2],res;
int find(int x) {
	return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void Union(int x,int y){
	x =find(x) ; y = find(y);
	fa[x] = y;
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m>>q;
	for(int i = 1;i<=n+m;i++)fa[i] = i;
	for(int i = 0;i<q;i++){
		cin>>u>>v;
		Union(u,v+n);
	}
	int rt = find(1);
	for(int i = 1;i<=n+m;i++)
		if(find(i) != rt) res++;
	cout<<res;
	return 0;
}


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

#define sz(x) (int)x.size()
#define pb push_back
#define mp make_pair
#define fi(a, b) for(int i=a; i<=b; i++)
#define fj(a, b) for(int j=a; j<=b; j++)
#define fo(a, b) for(int o=a; o<=b; o++)
#define fdi(a, b) for(int i=a; i>=b; i--)
#define fdj(a, b) for(int j=a; j>=b; j--)
#define fdo(a, b) for(int o=a; o>=b; o--)

#ifdef LOCAL
#define err(...) fprintf(stderr, __VA_ARGS__)
#else
#define err(...) while(false) {}
#endif

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<pii> vpii;
typedef vector<pll> vpll;
typedef long double ld;
typedef unsigned long long ull;

/////////////////////////////////

int const MAX = 2e5 + 41;

int n, m, q;

vi e[MAX][2];
int ans;
int u[MAX][2];
int x[MAX];
int y[MAX];

void dfs(int x, int p) {
	u[x][p] = 1;
	for (int y : e[x][p]) {
		if (!u[y][1 - p]) {
			dfs(y, 1 - p);
		}
	}
}

void solve() {
	if (n == 1) {
		set<int> t;
		fi(1, q) {
			t.insert(y[i]);
		}
		printf("%d\n", m - sz(t));
		return;
	}
	if (m == 1) {
		set<int> t;
		fi(1, q) {
			t.insert(x[i]);
		}
		printf("%d\n", n - sz(t));
		return;
	}

	ans = -1;
	fi(1, n) {
		if (!u[i][0]) {
			ans++;
			dfs(i, 0);
		}
	}
	fi(1, m) {
		if (!u[i][1]) {
			ans++;
			dfs(i, 1);
		}
	}
	printf("%d\n", ans);
}

int main() {
#ifdef LOCAL
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif

	scanf("%d %d %d", &n, &m, &q);
	fi(1, q) {
		scanf("%d %d", &x[i], &y[i]);
		e[x[i]][0].pb(y[i]);
		e[y[i]][1].pb(x[i]);
	}
	solve();

#ifdef LOCAL
	err("ELAPSED TIME: %.3Lf\n", (ld) clock() / CLOCKS_PER_SEC);
#endif

	return 0;
}


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

#define sz(x) (int)x.size()
#define pb push_back
#define mp make_pair
#define fi(a, b) for(int i=a; i<=b; i++)
#define fj(a, b) for(int j=a; j<=b; j++)
#define fo(a, b) for(int o=a; o<=b; o++)
#define fdi(a, b) for(int i=a; i>=b; i--)
#define fdj(a, b) for(int j=a; j>=b; j--)
#define fdo(a, b) for(int o=a; o>=b; o--)

#ifdef LOCAL
#define err(...) fprintf(stderr, __VA_ARGS__)
#else
#define err(...) while(false) {}
#endif

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<pii> vpii;
typedef vector<pll> vpll;
typedef long double ld;
typedef unsigned long long ull;

/////////////////////////////////

int const MAX = 2e5 + 41;
int const INF = 1e9 + 41;

int n, m, q;
int x[MAX];
int y[MAX];
bool row[MAX];
bool col[MAX];
int degr[MAX];
int degc[MAX];

void solve() {
	if (n == 1) {
		set<int> t;
		fi(1, q) {
			t.insert(y[i]);
		}
		printf("%d\n", m - sz(t));
		return;
	}
	if (m == 1) {
		set<int> t;
		fi(1, q) {
			t.insert(x[i]);
		}
		printf("%d\n", n - sz(t));
		return;
	}

	if (q == 0) {
		printf("%d\n", n + m - 2);
		return;
	}

	fi(1, q) {
		degr[x[i]]++;
		degc[y[i]]++;
	}

	int dr = 0;
	int dc = 0;
	fi(1, n) {
		if (degr[i] == 0) {
			dr++;
		}
	}
	fi(1, m) {
		if (degc[i] == 0) {
			dc++;
		}
	}

	int ans = INF;

	fi(1, q) {
		int ndr = dr;
		if (degr[x[i]] == 1) {
			ndr++;
		}
		int ndc = dc;
		if (degc[y[i]] == 1) {
			ndc++;
		}
		ans = min(ans, ndr + ndc);
	}
	printf("%d\n", ans);
}

int main() {
#ifdef LOCAL
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif

  scanf("%d %d %d", &n, &m, &q);
  fi(1, q) {
  	scanf("%d %d", &x[i], &y[i]);
  }
	solve();

#ifdef LOCAL
	err("ELAPSED TIME: %.3Lf\n", (ld) clock() / CLOCKS_PER_SEC);
#endif

	return 0;
}


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

#define sz(x) (int)x.size()
#define pb push_back
#define mp make_pair
#define fi(a, b) for(int i=a; i<=b; i++)
#define fj(a, b) for(int j=a; j<=b; j++)
#define fo(a, b) for(int o=a; o<=b; o++)
#define fdi(a, b) for(int i=a; i>=b; i--)
#define fdj(a, b) for(int j=a; j>=b; j--)
#define fdo(a, b) for(int o=a; o>=b; o--)

#ifdef LOCAL
#define err(...) fprintf(stderr, __VA_ARGS__)
#else
#define err(...) while(false) {}
#endif

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<pii> vpii;
typedef vector<pll> vpll;
typedef long double ld;
typedef unsigned long long ull;

/////////////////////////////////

int const MAX = 2e5 + 41;
int const INF = 1e9 + 41;

int n, m, q;
int x[MAX];
int y[MAX];
bool row[MAX];
bool col[MAX];
int degr[MAX];
int degc[MAX];

void solve() {
	if (n == 1) {
		set<int> t;
		fi(1, q) {
			t.insert(y[i]);
		}
		printf("%d\n", m - sz(t));
		return;
	}
	if (m == 1) {
		set<int> t;
		fi(1, q) {
			t.insert(x[i]);
		}
		printf("%d\n", n - sz(t));
		return;
	}

	if (q == 0) {
		printf("%d\n", n + m - 1);
		return;
	}

	fi(1, q) {
		degr[x[i]]++;
		degc[y[i]]++;
	}

	int dr = 0;
	int dc = 0;
	fi(1, n) {
		if (degr[i] == 0) {
			dr++;
		}
	}
	fi(1, m) {
		if (degc[i] == 0) {
			dc++;
		}
	}

	int ans = INF;

	fi(1, q) {
		int ndr = dr;
		bool f = 0;
		if (x[i] == 1 && y[i] == 1) {
			f = 1;
		}

		if (x[i] == 1 && y[i] == m) {
			f = 1;
		}

		if (x[i] == n && y[i] == 1) {
			f = 1;
		}

		if (x[i] == n && y[i] == m) {
			f = 1;
		}

		if (degr[x[i]] == 1 && !f) {
			ndr++;
		}
		int ndc = dc;
		if (degc[y[i]] == 1 && !f) {
			ndc++;
		}
		ans = min(ans, ndr + ndc);
	}
	printf("%d\n", ans);
}

int main() {
#ifdef LOCAL
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif

  scanf("%d %d %d", &n, &m, &q);
  fi(1, q) {
  	scanf("%d %d", &x[i], &y[i]);
  }
	solve();

#ifdef LOCAL
	err("ELAPSED TIME: %.3Lf\n", (ld) clock() / CLOCKS_PER_SEC);
#endif

	return 0;
}


#include<bits/stdc++.h>
using namespace std;
int fa[400005];
int vis[400005];
int find(int x)
{
	if(fa[x]==x) return fa[x];
	else return find(fa[x]);
}
void u(int x,int y)
{
	int xx=find(x);
	int yy=find(y);
	if(xx==yy) return ;
	fa[xx]=fa[yy];
}
int main()
{
	int n,m,q;
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n+m;i++) fa[i]=i;
	for(int i=0;i<q;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		if(find(x)==find(y+n)) continue;
		u(x,y+n);
	}
	int ans=0;
	for(int i=1;i<=n+m;i++)
		if(vis[find(i)]==0) ans++,vis[find(i)]=1;
	cout<<ans-1;
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
int fa[400005];
int find(int x)
{
	if(fa[x]==x) return fa[x];
	else return find(fa[x]);
}
void u(int x,int y)
{
	int xx=find(x);
	int yy=find(y);
	if(xx==yy) return ;
	fa[xx]=fa[yy];
}
int main()
{
	int n,m,q;
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n+m;i++) fa[i]=i;
	for(int i=0;i<q;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		if(find(x)==find(y+n)) continue;
		u(x,y+n);
	}
	int ans=0;
	for(int i=1;i<=n+m;i++)
		if(fa[i]==i) ans++;
	cout<<ans-1;
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
int fa[400005];
int vis[400005];
int find(int x)
{
	if(fa[x]==x) return fa[x];
	else return find(fa[x]);
}
void u(int x,int y)
{
	int xx=find(x);
	int yy=find(y);
	if(xx==yy) return ;
	fa[xx]=fa[yy];
}
int main()
{
	int n,m,q;
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n+m;i++) fa[i]=i;
	for(int i=0;i<q;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		u(x,y+n);
	}
	int ans=0;
	for(int i=1;i<=n+m;i++)
		if(vis[find(i)]==0) ans++,vis[find(i)]=1;
	cout<<ans-1;
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
int fa[400005];
int find(int x)
{
	if(fa[x]==x) return fa[x];
	else return fa[x]=find(fa[x]);
}
void u(int x,int y)
{
	int xx=find(x);
	int yy=find(y);
	if(xx==yy) return ;
	fa[xx]=fa[yy];
}
int main()
{
	int n,m,q;
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n+m;i++) fa[i]=i;
	for(int i=0;i<q;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		u(x,y+n);
	}
	int ans=0;
	for(int i=1;i<=n+m;i++)
		if(fa[i]==i) ans++;
	cout<<ans-1;
	return 0;
}


// Codeforces 1900-2200 difficulty. Problem #37
// DSU
#include<bits/stdc++.h>
#define N 200009
using namespace std;
bool rows[N], cols[N];
bool uniqueParents[N];
int parent[N];
int depth[N];
vector<pair<int, int>>indices;
void makeSets()
{
	for (int i = 0; i < N; i++)
	{
		parent[i] = i;
		depth[i] = 1;
	}
}
int findSet(int v)
{
	if (v == parent[v])
		return v;
	return parent[v] = findSet(parent[v]);
}
void unionSet(int u, int v)
{
	int a = findSet(u);
	int b = findSet(v);
	if (a != b)
	{
		if (depth[a] < depth[b])
			swap(a, b);
		parent[b] = a;
		if (depth[a] == depth[b])
			depth[a]++;
	}
}
int main()
{
	makeSets();
	int n, m, q;
	cin >> n >> m >> q;
	for (int i = 0; i < q; i++)
	{
		int l, r;
		cin >> l >> r;
		indices.push_back(make_pair(l, r));
		rows[l]	= true;
		cols[r] = true;
	}
	sort(indices.begin(), indices.end());
	for (int i = 1; i < q; i++)
		if (indices[i].first == indices[i - 1].first)
			unionSet(indices[i].second, indices[i - 1].second);
	int emptyRows = 0, emptyCols = 0;
	for (int i = 1; i <= n; i++)
		emptyRows += !rows[i];
	for (int i = 1; i <= m; i++)
	{
		if (!cols[i])
		{
			emptyCols++;
			parent[i] = -1;
		}
	}
	int ans = max(emptyRows, emptyCols);
	int sets = min(emptyRows, emptyCols);
	for (int i = 1; i <= m; i++)
	if (parent[i] != -1 && !uniqueParents[parent[i]])
	{
		sets++;
		uniqueParents[parent[i]] = true;
	}
	cout << sets + ans - 1 << endl;
}


// Codeforces 1900-2200 difficulty. Problem #37
// Implementation
#include<bits/stdc++.h>
#define N 200009
using namespace std;
bool rows[N], cols[N];
int main()
{
	int n, m, q;
	cin >> n >> m >> q;
	for (int i = 0; i < q; i++)
	{
		int l, r;
		cin >> l >> r;
		rows[l]	= true;
		cols[r] = true;
	}
	int ans = 0;
	for (int i = 1; i <= n; i++)
		ans += !rows[i];
	for (int i = 1; i <= m; i++)
		ans += !cols[i];
	cout << (ans == (n + m) ? ans - 1 : ans) << endl;
}


// Codeforces 1900-2200 difficulty. Problem #37
// DSU
#include<bits/stdc++.h>
#define N 200009
using namespace std;
bool rows[N], cols[N];
bool uniqueParents[N];
int parent[N];
int depth[N];
vector<pair<int, int>>indices;
void makeSets()
{
	for (int i = 0; i < N; i++)
	{
		parent[i] = i;
		depth[i] = 1;
	}
}
int findSet(int v)
{
	if (v == parent[v])
		return v;
	return parent[v] = findSet(parent[v]);
}
void unionSet(int u, int v)
{
	int a = findSet(u);
	int b = findSet(v);
	if (a != b)
	{
		if (depth[a] < depth[b])
			swap(a, b);
		parent[b] = a;
		if (depth[a] == depth[b])
			depth[a]++;
	}
}
int main()
{
	makeSets();
	int n, m, q;
	cin >> n >> m >> q;
	for (int i = 0; i < q; i++)
	{
		int l, r;
		cin >> l >> r;
		indices.push_back(make_pair(l, r));
		rows[l]	= true;
		cols[r] = true;
	}
	sort(indices.begin(), indices.end());
	for (int i = 1; i < q; i++)
		if (indices[i].first == indices[i - 1].first)
			unionSet(indices[i].second, indices[i - 1].second);
	int emptyRows = 0, emptyCols = 0;
	for (int i = 1; i <= n; i++)
		emptyRows += !rows[i];
	for (int i = 1; i <= m; i++)
	{
		if (!cols[i])
		{
			emptyCols++;
			parent[i] = -1;
		}
	}
	int ans = max(emptyRows, emptyCols);
	int sets = min(emptyRows, emptyCols);
	for (int i = 1; i <= m; i++)
	if (parent[i] != -1 && !uniqueParents[findSet(i)])
	{
		sets++;
		uniqueParents[findSet(i)] = true;
	}
	cout << sets + ans - 1 << endl;
}


#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <bitset>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>
#include <memory.h>
#include <functional>
#include <string.h>
#include <iomanip>
#include <unordered_set>
#include <unordered_map>
using namespace std;

typedef long long ll;
typedef pair<int,int> pi;
typedef double db;

const int mod=1e9+7;
ll qpow(ll x,ll k){return k==0?1:1ll*qpow(1ll*x*x%mod,k>>1)*(k&1?x:1)%mod;}
#define MP make_pair
#define FF first
#define SS second
#define LB lower_bound
#define UB upper_bound
#define PB push_back
#define lc ((p<<1)+1)
#define rc ((p<<1)+2)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rrep(i,b,a) for(int i=b;i>=a;i--)
#define all(v) (v).begin(),(v).end()
#define clean(v,a) memset(v,a,sizeof(v))
#define get(a) scanf("%d",&a)
#define get2(a,b) scanf("%d%d",&a,&b)
#define get3(a,b,c) scanf("%d%d%d",&a,&b,&c)

/*
 * high precision: printf("%.12lf",(db)ans);
 *
 * clear output buffer: fflush(stdout);
 *
 * sync off: ios::sync_with_stdio(false);
 *
 */

class pnt
{
public:
	int x,y,id;
	bool operator<(const pnt & o)const
	{
		return x<o.x||(x==o.x&&y<o.y);
	}
	void read()
	{
		scanf("%d%d",&x,&y);
	}
};

const int Maxn=2e5+5;

int n,m,q;
ll cnt=0;
pnt p[Maxn];
bool used[Maxn];
bool visx[Maxn];
bool visy[Maxn];
vector<int> adjx[Maxn];
vector<int> adjy[Maxn];
int w,h;
int totw=0,toth=0;

void dfs(int id)
{
	used[id]=true;
	if(!visx[p[id].x])
	{
		visx[p[id].x]=true;
		w++;
	}
	if(!visy[p[id].y])
	{
		visy[p[id].y]=true;
		h++;
	}
	for(auto v:adjx[p[id].x])
	{
		if(!used[v])
		{
			dfs(v);
		}
	}
	for(auto v:adjy[p[id].y])
	{
		if(!used[v])
		{
			dfs(v);
		}
	}
}

int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=q;i++)
	{
		p[i].read();
		adjx[p[i].x].PB(i);
		adjy[p[i].y].PB(i);
	}
	for(int i=1;i<=q;i++)
	{
		if(!used[i])
		{
			w=h=0;
			dfs(i);
			if(totw) cnt+=2;
			totw+=w;
			toth+=h;
		}
	}
	cnt+=(n-totw)+(m-toth);
	printf("%lld\n",cnt);
	return 0;
}


#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <bitset>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>
#include <memory.h>
#include <functional>
#include <string.h>
#include <iomanip>
#include <unordered_set>
#include <unordered_map>
using namespace std;

typedef long long ll;
typedef pair<int,int> pi;
typedef double db;

const int mod=1e9+7;
ll qpow(ll x,ll k){return k==0?1:1ll*qpow(1ll*x*x%mod,k>>1)*(k&1?x:1)%mod;}
#define MP make_pair
#define FF first
#define SS second
#define LB lower_bound
#define UB upper_bound
#define PB push_back
#define lc ((p<<1)+1)
#define rc ((p<<1)+2)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rrep(i,b,a) for(int i=b;i>=a;i--)
#define all(v) (v).begin(),(v).end()
#define clean(v,a) memset(v,a,sizeof(v))
#define get(a) scanf("%d",&a)
#define get2(a,b) scanf("%d%d",&a,&b)
#define get3(a,b,c) scanf("%d%d%d",&a,&b,&c)

/*
 * high precision: printf("%.12lf",(db)ans);
 *
 * clear output buffer: fflush(stdout);
 *
 * sync off: ios::sync_with_stdio(false);
 *
 */

class pnt
{
public:
	int x,y,id;
	bool operator<(const pnt & o)const
	{
		return x<o.x||(x==o.x&&y<o.y);
	}
	void read()
	{
		scanf("%d%d",&x,&y);
	}
};

const int Maxn=2e5+5;

int n,m,q;
ll cnt=0;
pnt p[Maxn];
bool used[Maxn];
bool visx[Maxn];
bool visy[Maxn];
vector<int> adjx[Maxn];
vector<int> adjy[Maxn];
int w,h;
int totw=0,toth=0;

void dfs(int id)
{
	used[id]=true;
	if(!visx[p[id].x])
	{
		visx[p[id].x]=true;
		w++;
		for(auto v:adjx[p[id].x])
		{
			if(!used[v])
			{
				dfs(v);
			}
		}
	}
	if(!visy[p[id].y])
	{
		visy[p[id].y]=true;
		h++;
		for(auto v:adjy[p[id].y])
		{
			if(!used[v])
			{
				dfs(v);
			}
		}
	}
}

int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=q;i++)
	{
		p[i].read();
		adjx[p[i].x].PB(i);
		adjy[p[i].y].PB(i);
	}
	for(int i=1;i<=q;i++)
	{
		if(!used[i])
		{
			w=h=0;
			dfs(i);
			if(totw) cnt++;
			totw+=w;
			toth+=h;
		}
	}
	if(totw==0)
	{
		cnt+=n+m-1;
	}
	else
	{
		cnt+=(n-totw)+(m-toth);
	}
	printf("%lld\n",cnt);
	return 0;
}


#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <bitset>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>
#include <memory.h>
#include <functional>
#include <string.h>
#include <iomanip>
#include <unordered_set>
#include <unordered_map>
using namespace std;

typedef long long ll;
typedef pair<int,int> pi;
typedef double db;

const int mod=1e9+7;
ll qpow(ll x,ll k){return k==0?1:1ll*qpow(1ll*x*x%mod,k>>1)*(k&1?x:1)%mod;}
#define MP make_pair
#define FF first
#define SS second
#define LB lower_bound
#define UB upper_bound
#define PB push_back
#define lc ((p<<1)+1)
#define rc ((p<<1)+2)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rrep(i,b,a) for(int i=b;i>=a;i--)
#define all(v) (v).begin(),(v).end()
#define clean(v,a) memset(v,a,sizeof(v))
#define get(a) scanf("%d",&a)
#define get2(a,b) scanf("%d%d",&a,&b)
#define get3(a,b,c) scanf("%d%d%d",&a,&b,&c)

/*
 * high precision: printf("%.12lf",(db)ans);
 *
 * clear output buffer: fflush(stdout);
 *
 * sync off: ios::sync_with_stdio(false);
 *
 */

class pnt
{
public:
	int x,y,id;
	bool operator<(const pnt & o)const
	{
		return x<o.x||(x==o.x&&y<o.y);
	}
	void read()
	{
		scanf("%d%d",&x,&y);
	}
};

const int Maxn=2e5+5;

int n,m,q;
ll cnt=0;
pnt p[Maxn];
bool used[Maxn];
bool visx[Maxn];
bool visy[Maxn];
vector<int> adjx[Maxn];
vector<int> adjy[Maxn];
int w,h;
int totw=0,toth=0;

void dfs(int id)
{
	used[id]=true;
	if(!visx[p[id].x])
	{
		visx[p[id].x]=true;
		w++;
	}
	if(!visy[p[id].y])
	{
		visy[p[id].y]=true;
		h++;
	}
	for(auto v:adjx[p[id].x])
	{
		if(!used[v])
		{
			dfs(v);
		}
	}
	for(auto v:adjy[p[id].y])
	{
		if(!used[v])
		{
			dfs(v);
		}
	}
}

int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=q;i++)
	{
		p[i].read();
		adjx[p[i].x].PB(i);
		adjy[p[i].y].PB(i);
	}
	for(int i=1;i<=q;i++)
	{
		if(!used[i])
		{
			w=h=0;
			dfs(i);
			if(totw) cnt++;
			totw+=w;
			toth+=h;
		}
	}
	if(totw==0)
	{
		cnt+=n+m-1;
	}
	else
	{
		cnt+=(n-totw)+(m-toth);
	}
	printf("%lld\n",cnt);
	return 0;
}


#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <bitset>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>
#include <memory.h>
#include <functional>
#include <string.h>
#include <iomanip>
#include <unordered_set>
#include <unordered_map>
using namespace std;

typedef long long ll;
typedef pair<int,int> pi;
typedef double db;

const int mod=1e9+7;
ll qpow(ll x,ll k){return k==0?1:1ll*qpow(1ll*x*x%mod,k>>1)*(k&1?x:1)%mod;}
#define MP make_pair
#define FF first
#define SS second
#define LB lower_bound
#define UB upper_bound
#define PB push_back
#define lc ((p<<1)+1)
#define rc ((p<<1)+2)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rrep(i,b,a) for(int i=b;i>=a;i--)
#define all(v) (v).begin(),(v).end()
#define clean(v,a) memset(v,a,sizeof(v))
#define get(a) scanf("%d",&a)
#define get2(a,b) scanf("%d%d",&a,&b)
#define get3(a,b,c) scanf("%d%d%d",&a,&b,&c)

/*
 * high precision: printf("%.12lf",(db)ans);
 *
 * clear output buffer: fflush(stdout);
 *
 * sync off: ios::sync_with_stdio(false);
 *
 */

class pnt
{
public:
	int x,y,id;
	bool operator<(const pnt & o)const
	{
		return x<o.x||(x==o.x&&y<o.y);
	}
	void read()
	{
		scanf("%d%d",&x,&y);
	}
};

const int Maxn=2e5+5;

int n,m,q;
ll cnt=0;
pnt p[Maxn];
bool used[Maxn];
bool visx[Maxn];
bool visy[Maxn];
vector<int> adjx[Maxn];
vector<int> adjy[Maxn];
int w,h;
int totw,toth;

void dfs(int id)
{
	used[id]=true;
	if(!visx[p[id].x])
	{
		visx[p[id].x]=true;
		w++;
	}
	if(!visy[p[id].y])
	{
		visy[p[id].y]=true;
		h++;
	}
	for(auto v:adjx[p[id].x])
	{
		if(!used[v])
		{
			dfs(v);
		}
	}
	for(auto v:adjy[p[id].y])
	{
		if(!used[v])
		{
			dfs(v);
		}
	}
}

int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=q;i++)
	{
		p[i].read();
		adjx[p[i].x].PB(i);
		adjy[p[i].y].PB(i);
	}
	for(int i=1;i<=q;i++)
	{
		if(!used[i])
		{
			w=h=0;
			dfs(i);
			if(totw) cnt+=2;
			totw+=w;
			toth+=h;
		}
	}
	cnt+=(n-totw)+(m-toth);
	printf("%lld\n",cnt);
	return 0;
}


#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <algorithm>
#include <functional>
#include <utility>
#include <bitset>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <cstdio>
#include <memory.h>
#include <functional>
#include <string.h>
#include <iomanip>
#include <unordered_set>
#include <unordered_map>
using namespace std;

typedef long long ll;
typedef pair<int,int> pi;
typedef double db;

const int mod=1e9+7;
ll qpow(ll x,ll k){return k==0?1:1ll*qpow(1ll*x*x%mod,k>>1)*(k&1?x:1)%mod;}
#define MP make_pair
#define FF first
#define SS second
#define LB lower_bound
#define UB upper_bound
#define PB push_back
#define lc ((p<<1)+1)
#define rc ((p<<1)+2)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rrep(i,b,a) for(int i=b;i>=a;i--)
#define all(v) (v).begin(),(v).end()
#define clean(v,a) memset(v,a,sizeof(v))
#define get(a) scanf("%d",&a)
#define get2(a,b) scanf("%d%d",&a,&b)
#define get3(a,b,c) scanf("%d%d%d",&a,&b,&c)

/*
 * high precision: printf("%.12lf",(db)ans);
 *
 * clear output buffer: fflush(stdout);
 *
 * sync off: ios::sync_with_stdio(false);
 *
 */

class pnt
{
public:
	int x,y,id;
	bool operator<(const pnt & o)const
	{
		return x<o.x||(x==o.x&&y<o.y);
	}
	void read()
	{
		scanf("%d%d",&x,&y);
	}
};

const int Maxn=2e5+5;

int n,m,q;
ll cnt=0;
pnt p[Maxn];
bool used[Maxn];
bool visx[Maxn];
bool visy[Maxn];
vector<int> adjx[Maxn];
vector<int> adjy[Maxn];
int w,h;
int totw=0,toth=0;

void dfs(int id)
{
	used[id]=true;
	if(!visx[p[id].x])
	{
		visx[p[id].x]=true;
		w++;
	}
	if(!visy[p[id].y])
	{
		visy[p[id].y]=true;
		h++;
	}
	for(auto v:adjx[p[id].x])
	{
		if(!used[v])
		{
			dfs(v);
		}
	}
	for(auto v:adjy[p[id].y])
	{
		if(!used[v])
		{
			dfs(v);
		}
	}
}

int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=q;i++)
	{
		p[i].read();
		adjx[p[i].x].PB(i);
		adjy[p[i].y].PB(i);
	}
	for(int i=1;i<=q;i++)
	{
		if(!used[i])
		{
			w=h=0;
			dfs(i);
			if(totw) cnt+=2;
			totw+=w;
			toth+=h;
		}
	}
	if(totw==0)
	{
		cnt+=n+m-1;
	}
	else
	{
		cnt+=(n-totw)+(m-toth);
	}
	printf("%lld\n",cnt);
	return 0;
}


#include<bits/stdc++.h>
#define MAXN 200010
using namespace std;

struct {
	int p[MAXN];
	void init(){
		for(int i = 0; i < MAXN; i++)
			p[i] = i;
	}

	int _find(int u){
		if(p[u] == u)
			return u;
		return p[u] = _find(p[u]);
	}

	void _union(int a, int b){
		a = _find(a);
		b = _find(b);
		if(a == b) return ;
		p[b] = a;
	}

} uf[2];

vector<int> li[MAXN], co[MAXN];

int mark_l[MAXN], mark_c[MAXN];
int cont_l, cont_c;
int main(){
	int N, M, Q;
	cin >> N >> M >> Q;

	uf[0].init();
	uf[1].init();
	int i, j;
	for(int l = 0; l < Q; l++){
		cin >> i >> j;
		co[i].push_back(j);
		li[j].push_back(i);

		if(!mark_l[i])
			cont_l++, mark_l[i] = 1;
		if(!mark_c[j])
			cont_c++, mark_c[j] = 1;
	}
	for(int i = 0; i < MAXN; i++){
		for(int j = 1; j < li[i].size(); j++){
			uf[0]._union(li[i][j], li[i][j-1]);
			}
		for(int j = 1; j < co[i].size(); j++){
			uf[1]._union(co[i][j], co[i][j-1]);
		}
	}
	int ma_l = 0, ma_c = 0;
	for(int i = 1; i <= N; i++)
		ma_l += uf[0]._find(i) == i;
	for(int i = 1; i <= M; i++)
		ma_c += uf[1]._find(i) == i;
 	long long ans = min((N - cont_l) + ma_c - 1, (M - cont_c) + ma_l - 1);
	cout << ans << "\n";
	return 0;
}


#include<bits/stdc++.h>
#define MAXN 200010
using namespace std;

struct {
	int p[MAXN];
	int c[MAXN];
	void init(){
		for(int i = 0; i < MAXN; i++)
			c[i] = 1, p[i] = i;
	}

	int _find(int u){
		if(p[u] == u)
			return u;
		return p[u] = _find(p[u]);
	}

	void _union(int a, int b){
		a = _find(a);
		b = _find(b);
		if(a == b) return ;
		c[a] += c[b];
		p[b] = a;
	}

	int C(int a){
		a = _find(a);
		return c[a];
	}
} uf[2];

vector<int> li[MAXN], co[MAXN];

int mark_l[MAXN], mark_c[MAXN];
int cont_l, cont_c;
int main(){
	int N, M, Q;
	cin >> N >> M >> Q;

	uf[0].init();
	uf[1].init();
	int i, j;
	for(int l = 0; l < Q; l++){
		cin >> i >> j;
		co[i].push_back(j);
		li[j].push_back(i);

		if(!mark_l[i])
			cont_l++, mark_l[i] = 1;
		if(!mark_c[j])
			cont_c++, mark_c[j] = 1;
	}
	int ma_l = 1, ma_c = 1;
	for(int i = 0; i < MAXN; i++){
		for(int j = 1; j < li[i].size(); j++){
			uf[0]._union(li[i][j], li[i][j-1]);
			ma_l = max(ma_l, uf[0].C(li[i][j]));
		}
		for(int j = 1; j < co[i].size(); j++){
			uf[1]._union(co[i][j], co[i][j-1]);
			ma_c = max(ma_c, uf[1].C(co[i][j]));
		}
	}
	//cout << cont_l << " " << cont_c << " " << ma_l << " " << ma_c << endl;
 	long long ans = min((N - cont_l) + (M - ma_c), (M - cont_c) + (N - ma_l));
	cout << ans << "\n";
	return 0;
}


#include<bits/stdc++.h>
#define MAXN 200010
using namespace std;

struct {
	int p[MAXN];
	int c[MAXN];
	void init(){
		for(int i = 0; i < MAXN; i++)
			c[i] = 1, p[i] = i;
	}

	int _find(int u){
		if(p[u] == u)
			return u;
		return p[u] = _find(p[u]);
	}

	void _union(int a, int b){
		a = _find(a);
		b = _find(b);
		if(a == b) return ;
		c[a] += c[b];
		p[b] = a;
	}

	int C(int a){
		a = _find(a);
		return c[a];
	}
} uf[2];

vector<int> li[MAXN], co[MAXN];

int mark_l[MAXN], mark_c[MAXN];
int cont_l, cont_c;
int main(){
	int N, M, Q;
	cin >> N >> M >> Q;

	uf[0].init();
	uf[1].init();
	int i, j;
	for(int l = 0; l < Q; l++){
		cin >> i >> j;
		co[i].push_back(j);
		li[j].push_back(i);

		if(!mark_l[i])
			cont_l++, mark_l[i] = 1;
		if(!mark_c[j])
			cont_c++, mark_c[j] = 1;
	}
	int ma_l = 1, ma_c = 1;
	for(int i = 0; i < MAXN; i++){
		for(int j = 1; j < li[i].size(); j++){
			uf[0]._union(li[i][j], li[i][j-1]);
			ma_l = uf[0].C(li[i][j]);
		}
		for(int j = 1; j < co[i].size(); j++){
			uf[1]._union(co[i][j], co[i][j-1]);
			ma_c = uf[1].C(co[i][j]);
		}
	}
	//cout << cont_l << " " << cont_c << " " << ma_l << " " << ma_c << endl;
 	long long ans = min((N - cont_l) + (M - ma_c), (M - cont_c) + (N - ma_l));
	cout << ans << "\n";
	return 0;
}


#include<bits/stdc++.h>
#define MAXN 200010
using namespace std;

struct {
	int p[MAXN];
	int c[MAXN];
	void init(){
		for(int i = 0; i < MAXN; i++)
			c[i] = 1, p[i] = i;
	}

	int _find(int u){
		if(p[u] == u)
			return u;
		return p[u] = _find(p[u]);
	}

	void _union(int a, int b){
		a = _find(a);
		b = _find(b);
		if(a == b) return ;
		c[a] += c[b];
		p[b] = a;
	}

	int C(int a){
		a = _find(a);
		return c[a];
	}
} uf[2];

vector<int> li[MAXN], co[MAXN];

int mark_l[MAXN], mark_c[MAXN];
int cont_l, cont_c;
int main(){
	int N, M, Q;
	cin >> N >> M >> Q;

	uf[0].init();
	uf[1].init();
	int i, j;
	for(int l = 0; l < Q; l++){
		cin >> i >> j;
		co[i].push_back(j);
		li[j].push_back(i);

		if(!mark_l[i])
			cont_l++, mark_l[i] = 1;
		if(!mark_c[j])
			cont_c++, mark_c[j] = 1;
	}
	int ma_l = 1, ma_c = 1;
	for(int i = 0; i < MAXN; i++){
		for(int j = 1; j < li[i].size(); j++){
			uf[0]._union(li[i][j], li[i][j-1]);
			ma_l = uf[0].C(li[i][j]);
		}
		for(int j = 1; j < co[i].size(); j++){
			uf[1]._union(co[i][j], co[i][j-1]);
			ma_c = uf[1].C(li[i][j]);
		}
	}
	//cout << cont_l << " " << cont_c << " " << ma_l << " " << ma_c << endl;
 	long long ans = min((N - cont_l) + (M - ma_c), (M - cont_c) + (N - ma_l));
	cout << ans << "\n";
	return 0;
}


#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 200005 * 2;
int pre[maxn];
int find(int x)
{
    while (x != pre[x])
        x = pre[x];
    return x;
}
void unit(int x, int y)
{
    x = find(x);
    y = find(y);
    if (x != y)
        pre[y] = x;
}
int main()
{
    int n, m, q;
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 0; i <= n + m + 1; i++)
    {
        pre[i] = i;
    }
    for (int i = 1; i <= q; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        unit(x, y+n);
    }
    long long ans=0;
    int root = find(1);
    for (int i = 1; i <= n + m;i++)
    {
        int p = find(i);
        if(p!=root)
        {
            ans++;
            pre[p] = root;
        }
    }
    printf("%lld", ans);
    return 0;
}


#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 200005 * 2;
int pre[maxn];
int find(int x)
{
    return pre[x] == x ? x : pre[x] = find(pre[x]);
}
void unit(int x, int y)
{
    x = find(x);
    y = find(y);
    if (x != y)
        pre[y] = x;
}
int main()
{
    int n, m, q;
    int x, y;
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 0; i <= n + m + 1; i++)
    {
        pre[i] = i;
    }
    for (int i = 1; i <= q; i++)
    {
        scanf("%d%d", &x, &y);
        unit(x, y+n);
    }
    long long ans=0;
    int root = find(1);
    for (int i = 1; i <= n + m;i++)
    {
        int p = find(i);
        if(p!=root)
        {
            ans++;
            pre[p] = root;
        }
    }
    printf("%lld", ans);
    return 0;
}


#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 200005 * 2;
int pre[maxn];
int find(int x)
{
    while (x != pre[x])
        x = pre[x];
    return x;
}
void unit(int x, int y)
{
    x = find(x);
    y = find(y);
    if (x != y)
        pre[y] = x;
}
int main()
{
    int n, m, q;
    cin >> n >> m >> q;
    for (int i = 0; i <= n + m + 1; i++)
    {
        pre[i] = i;
    }
    for (int i = 1; i <= q; i++)
    {
        int x, y;
        cin >> x >> y;
        unit(x, y+n);
    }
    int ans=0;
    int root = find(1);
    for (int i = 1; i <= n + m;i++)
    {
        int p = find(i);
        if(p!=root)
        {
            ans++;
            pre[p] = root;
        }
    }
    cout << ans;
    return 0;
}


#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 200005 * 2;
int pre[maxn];
int find(int x)
{
    while (x != pre[x])
        x = pre[x];
    return x;
}
void unit(int x, int y)
{
    x = find(x);
    y = find(y);
    if (x != y)
        pre[y] = x;
}
int main()
{
    int n, m, q;
    int x, y;
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 0; i <= n + m + 1; i++)
    {
        pre[i] = i;
    }
    for (int i = 1; i <= q; i++)
    {
        scanf("%d%d", &x, &y);
        unit(x, y+n);
    }
    long long ans=0;
    int root = find(1);
    for (int i = 1; i <= n + m;i++)
    {
        int p = find(i);
        if(p!=root)
        {
            ans++;
            pre[p] = root;
        }
    }
    printf("%lld", ans);
    return 0;
}


#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
const int maxn = 4e5 + 10;
int f[maxn];
int find(int x) {
	return f[x] == x ? x : f[x] = find(f[x]);
}
void unite(int x, int y) {
	int xx = find(x);
	int yy = find(y);
	if (xx != yy)
		f[xx] = yy;
}
void init(int n) {
	for (int i = 1; i <= n; i++)
		f[i] = i;
}
int main() {
	int n, m, q, x, y;
	cin >> n >> m >> q;
	init(n + m);
	for (int i = 0; i < q; i++) {
		cin >> x >> y;
		unite(x, y + n);          //注意这里是y+n,目的:将纵坐标1~m编号为n+1~n+m
	}
	int ans = 0;
	int root = find(1);           //选一个点的根作为root节点
	for (int i = 2; i <= n + m; i++) {
		if (find(i) != root) {    //若是该链是孤立的,将其根直接连到根上(表示为图中增加一个点从而获得连通关系)
			ans++;
			f[find(i)] = root;
		}
	}
	cout << ans << endl;
	return 0;
}


#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 200005 * 2;
int pre[maxn];
int find(int x)
{
    while (x != pre[x])
        x = pre[x];
    return x;
}
void unit(int x, int y)
{
    x = find(x);
    y = find(y);
    if (x != y)
        pre[x] = y;
}
int main()
{
    int n, m, q;
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 0; i <= n + m + 1; i++)
    {
        pre[i] = i;
    }
    for (int i = 1; i <= q; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        unit(x, y+n);
    }
    long long ans=0;
    int root = find(1);
    for (int i = 1; i <= n + m;i++)
    {
        int p = find(i);
        if(p!=root)
        {
            ans++;
            pre[p] = root;
        }
    }
    printf("%lld", ans);
    return 0;
}


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

// io
#define get(i) scanf("%d", &i)
#define flush fflush(stdin)

// manipulations
#define f(i, n) for (int i = 0; i < n; i++)
#define fr(i, n) for (int i = n - 1; i >= 0; i--)
#define ff(i, a, b) for (int i = a; i <= b; i++)
#define lowbit(x) (x & -x)
#define sz(x) int((x).size())

// built-in
#define bitcount __builtin_popcount
#define gcd __gcd

// short
#define pb push_back
#define fill(arr, a) memset(arr, a, sizeof(arr))

// useful bits
#define IN(i, l, r) (l <= i && i <= r)

typedef vector<int> VI;
typedef pair<int, int> PII;
typedef long long ll;

const ll mod = (ll) (1e9 + 7);
ll modpow(ll a, ll b) {if (!b) return 1; a %= mod; return modpow(a * a % mod, b / 2) * (b & 1 ? a : 1) % mod;}

int dsu[400000], sz[400000];

void init() {
	f(i, 400000) dsu[i] = i;
	fill(sz, 0);
}

int root(int v) {return dsu[v] = v == dsu[v] ? v : root(dsu[v]);}

void merge(int a, int b) {
	if ((a = root(a)) == (b = root(b))) return;
	if (sz[a] < sz[b]) swap(a, b);
	dsu[b] = a; sz[a] += sz[b];
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);

	int n, m, q; get(n); get(m); get(q);

	set<int> cols[n];

	set<int> included;

	f(i, q) {
		int x, y; get(x); get(y);
		cols[--x].insert(--y);
		merge(x, y + n);
		included.insert(y);
	}
	bool vis[400000]; fill(vis, false);

	int cnt = 0;
	f(i, n) {
		if (!vis[root(i)])
			cnt++;
		vis[root(i)] = true;
	}
	cnt--;

	cout << int(cnt + m - included.size()) << endl;

	return 0;
}


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

// io
#define get(i) scanf("%d", &i)
#define flush fflush(stdin)

// manipulations
#define f(i, n) for (int i = 0; i < n; i++)
#define fr(i, n) for (int i = n - 1; i >= 0; i--)
#define ff(i, a, b) for (int i = a; i <= b; i++)
#define lowbit(x) (x & -x)
#define sz(x) int((x).size())

// built-in
#define bitcount __builtin_popcount
#define gcd __gcd

// short
#define pb push_back
#define fill(arr, a) memset(arr, a, sizeof(arr))

// useful bits
#define IN(i, l, r) (l <= i && i <= r)

typedef vector<int> VI;
typedef pair<int, int> PII;
typedef long long ll;

const ll mod = (ll) (1e9 + 7);
ll modpow(ll a, ll b) {if (!b) return 1; a %= mod; return modpow(a * a % mod, b / 2) * (b & 1 ? a : 1) % mod;}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);

	int n, m, q; get(n); get(m); get(q);

	set<int> cols[n];
	f(i, q) {
		int x, y; get(x); get(y); x--; y--;
		cols[x].insert(y);
	}

	bool vis[m]; int sz = 0;
	for (int y : cols[0]) vis[y] = true;
	sz += cols[0].size();

	int cost = 0;
	ff(i, 1, n - 1) {
		if (cols[i].empty())
			cost++;
		else {
			int t = cols[i].size();
			int l = 0;
			for (int y : cols[i]) {
				if (!vis[y]) l++;
				vis[y] = true;
			}
			if (l == t) cost++;
			sz += l;
		}
	}

	cout << cost + (m - sz) << endl;

	return 0;
}


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

// io
#define get(i) scanf("%d", &i)
#define flush fflush(stdin)

// manipulations
#define f(i, n) for (int i = 0; i < n; i++)
#define fr(i, n) for (int i = n - 1; i >= 0; i--)
#define ff(i, a, b) for (int i = a; i <= b; i++)
#define lowbit(x) (x & -x)
#define sz(x) int((x).size())

// built-in
#define bitcount __builtin_popcount
#define gcd __gcd

// short
#define pb push_back
#define fill(arr, a) memset(arr, a, sizeof(arr))

// useful bits
#define IN(i, l, r) (l <= i && i <= r)

typedef vector<int> VI;
typedef pair<int, int> PII;
typedef long long ll;

const ll mod = (ll) (1e9 + 7);
ll modpow(ll a, ll b) {if (!b) return 1; a %= mod; return modpow(a * a % mod, b / 2) * (b & 1 ? a : 1) % mod;}

int dsu[400000], sz[400000];

void init() {
	f(i, 400000) dsu[i] = i;
	fill(sz, 1);
}

int root(int v) {return dsu[v] = v == dsu[v] ? v : root(dsu[v]);}

void merge(int a, int b) {
	if ((a = root(a)) == (b = root(b))) return;
	if (sz[a] < sz[b]) swap(a, b);
	dsu[b] = a; sz[a] += sz[b];
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);

	int n, m, q; get(n); get(m); get(q);

	set<int> cols[n];

	init();

	set<int> included;

	f(i, q) {
		int x, y; get(x); get(y);
		cols[--x].insert(--y);
		merge(x, y + n);
		included.insert(y);
	}
	bool vis[400000]; fill(vis, false);

	int cnt = 0;
	f(i, n) {
		if (!vis[root(i)]) cnt++;
		vis[root(i)] = true;
	}

	cout << int(--cnt + m - included.size()) << endl;

	return 0;
}


#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <bitset>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <sstream>
#include <stack>
#include <iomanip>
using namespace std;
#define pb push_back
#define mp make_pair
typedef pair<int,int> pii;
typedef long long ll;
typedef double ld;
typedef vector<int> vi;
#define fi first
#define se second
#define fe first
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
#define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
#define es(x,e) (int e=fst[x];e;e=nxt[e])
#define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
#define SZ 666666
struct UFS
{
int ff[SZ];
int gf(int x) {return ff[x]?ff[x]=gf(ff[x]):x;}
bool uni(int a,int b)
{
	a=gf(a),b=gf(b);
	if(a!=b) return ff[a]=b,1;
	return 0;
}
}X,Y;
int n,m,q;
vector<int> xs[SZ],ys[SZ];
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	if(!q)
	{
		printf("%d\n",n+m-1);
		return 0;
	}
	for(int i=1;i<=q;++i)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		xs[y].pb(x); ys[x].pb(y);
	}
	int aa=n-1,bb=m-1;
	for(int i=1;i<=m;++i)
	{
		int ls=-1;
		for(auto x:xs[i])
		{
			if(ls!=-1) aa-=X.uni(ls,x); ls=x;
		}
	}
	for(int i=1;i<=n;++i)
	{
		int ls=-1;
		for(auto x:ys[i])
		{
			if(ls!=-1) bb-=Y.uni(ls,x); ls=x;
		}
	}
	printf("%d\n",aa+bb-(aa&&bb));
}


#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <bitset>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <sstream>
#include <stack>
#include <iomanip>
using namespace std;
#define pb push_back
#define mp make_pair
typedef pair<int,int> pii;
typedef long long ll;
typedef double ld;
typedef vector<int> vi;
#define fi first
#define se second
#define fe first
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
#define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
#define es(x,e) (int e=fst[x];e;e=nxt[e])
#define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
#define SZ 666666
struct UFS
{
int ff[SZ];
int gf(int x) {return ff[x]?ff[x]=gf(ff[x]):x;}
bool uni(int a,int b)
{
	a=gf(a),b=gf(b);
	if(a!=b) return ff[a]=b,1;
	return 0;
}
}X,Y;
int n,m,q;
vector<int> xs[SZ],ys[SZ];
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	if(!q)
	{
		printf("%d\n",n+m-1);
		return 0;
	}
	for(int i=1;i<=q;++i)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		xs[y].pb(x); ys[x].pb(y);
	}
	int aa=n-1,bb=m-1;
	for(int i=1;i<=m;++i)
	{
		int ls=-1;
		for(auto x:xs[i])
		{
			if(ls!=-1) aa-=X.uni(ls,x); ls=x;
		}
	}
	for(int i=1;i<=n;++i)
	{
		int ls=-1;
		for(auto x:ys[i])
		{
			if(ls!=-1) bb-=Y.uni(ls,x); ls=x;
		}
	}
	printf("%d\n",aa+bb);
}


#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <bitset>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <algorithm>
#include <sstream>
#include <stack>
#include <iomanip>
using namespace std;
#define pb push_back
#define mp make_pair
typedef pair<int,int> pii;
typedef long long ll;
typedef double ld;
typedef vector<int> vi;
#define fi first
#define se second
#define fe first
#define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
#define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
#define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
#define es(x,e) (int e=fst[x];e;e=nxt[e])
#define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e])
#define SZ 666666
struct UFS
{
int ff[SZ];
int gf(int x) {return ff[x]?ff[x]=gf(ff[x]):x;}
bool uni(int a,int b)
{
	a=gf(a),b=gf(b);
	if(a!=b) return ff[a]=b,1;
	return 0;
}
}X;
int n,m,q;
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	int ans=n+m-1;
	for(int i=1;i<=q;++i)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		ans-=X.uni(x,y+n);
	}
	printf("%d\n",ans);
}


#include <bits/stdc++.h>

using namespace std;

int m,n;

struct DSU
{
    pair<int, int> f[400001];

    void init ()
    {
        for (int i=1; i<=m; i++)
        {
            f[i] = {-1, 0};
        }
        for (int i=m+1; i<=m+n; i++)
        {
            f[i] = {-1, 0};
        }

    }

    int root(int u)
    {
        if (f[u].first <= 0)
            return u;
        return f[u].first=root(f[u].first);
    }

    void mtree (int ru, int rv)
    {
        f[ru].first += f[rv].first;
        f[ru].second += f[rv].second;
        f[rv] = {ru,0};
    }
}dsu;
int q;
int main()
{
    ios_base::sync_with_stdio(0);
    cin >> m >> n >> q;
    for (int x,y,rx,ry,i=1; i<=q; i++)
    {
        cin >> x >> y;
        rx=dsu.root(x);
        ry=dsu.root(y+m);
        if (rx!=ry) dsu.mtree(rx,ry);
    }
    long long rr = m*(long long)n;
    for (int i=1; i<=m+n;++i)
    {
        if (dsu.f[i].first > 0)
        {
            rr -= dsu.f[i].first * (long long)dsu.f[i].second;
        }
    }
    cout << rr;
    return 0;
}


#include <bits/stdc++.h>

using namespace std;

int m,n,q;

struct TP
{
    int first, second;
};

struct DSU
{

    TP f[400001];

    void init ()
    {
        for (int i=1; i<=m; i++)
        {
            f[i] = {-1, 0};
        }
        for (int i=m+1; i<=m+n; i++)
        {
            f[i] = {0, -1};
        }

    }

    int root(int u)
    {
        if (f[u].first <= 0)
            return u;
        return f[u].first=root(f[u].first);
    }

    void mtree (int ru, int rv)
    {
        f[ru].first += f[rv].first;
        f[ru].second += f[rv].second;
        f[rv] = {ru,0};
    }
}dsu;

int main()
{
    ios_base::sync_with_stdio(0);
    cin >> m >> n >> q;
    dsu.init();
    for (int x,y,rx,ry,i=1; i<=q; i++)
    {
        cin >> x >> y;
        rx=dsu.root(x);
        ry=dsu.root(y+m);
        if (rx!=ry) dsu.mtree(rx,ry);
    }
    long long rr = m*(long long)n; int tt = 0;
    for (int i=1; i<=m+n;++i)
    {
        if (dsu.f[i].first < 0 || dsu.f[i].second < 0)
        {
            rr -= dsu.f[i].first * (long long)dsu.f[i].second;
            tt++;
        }
    }
    cout << tt-1;
    return 0;
}


#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

void dfs_comp(
    vector<int> const& r, vector<int> const& c, vector<vector<int>> const& rcs, vector<vector<int>> const& crs,
    int v, int ncomp, vector<int>& comp
) {
    comp[v] = ncomp;
    for (int i : rcs[r[v]]) {
        if (comp[i] == -1) {
            dfs_comp(r, c, rcs, crs, i, ncomp, comp);
        }
    }
    for (int i : crs[c[v]]) {
        if (comp[i] == -1) {
            dfs_comp(r, c, rcs, crs, i, ncomp, comp);
        }
    }
}

int main()
{
    cin.tie(0);
    cin.sync_with_stdio(false);

    int n, m, cnt;
    cin >> n >> m >> cnt;
    vector<int> r(cnt), c(cnt);
    vector<vector<int>> rcs(n), crs(m);
    for (int i = 0; i < cnt; ++i) {
        cin >> r[i] >> c[i]; --r[i]; --c[i];
        rcs[r[i]].push_back(i);
        crs[c[i]].push_back(i);
    }

    //for (int i = 0; i < n; ++i) { sort(rcs[i].begin(), rcs[i].end()); }
    //for (int i = 0; i < m; ++i) { sort(crs[i].begin(), crs[i].end()); }

    vector<int> comp(cnt, -1);
    int ncomp = 0;
    for (int i = 0; i < cnt; ++i) {
        if (comp[i] == -1) {
            dfs_comp(r, c, rcs, crs, i, ncomp, comp);
            ncomp += 1;
        }
    }

    int emptyr = 0, emptyc = 0;
    for (int i = 0; i < n; ++i) {
        if (rcs[i].empty()) { emptyr += 1; }
    }
    for (int i = 0; i < m; ++i) {
        if (crs[i].empty()) { emptyc += 1; }
    }

    cout << ncomp - 1 + emptyr + emptyc << endl;
}


#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

void dfs_comp(
    vector<int> const& r, vector<int> const& c, vector<vector<int>> const& rcs, vector<vector<int>> const& crs,
    int v, int ncomp, vector<int>& comp
) {
    comp[v] = ncomp;
    for (int i : rcs[r[v]]) {
        if (comp[i] == -1) {
            dfs_comp(r, c, rcs, crs, i, ncomp, comp);
        }
    }
    for (int i : crs[c[v]]) {
        if (comp[i] == -1) {
            dfs_comp(r, c, rcs, crs, i, ncomp, comp);
        }
    }
}

int main()
{
    cin.tie(0);
    cin.sync_with_stdio(false);

    int n, m, cnt;
    cin >> n >> m >> cnt;
    vector<int> r(cnt), c(cnt);
    vector<vector<int>> rcs(n), crs(m);
    for (int i = 0; i < cnt; ++i) {
        cin >> r[i] >> c[i]; --r[i]; --c[i];
        rcs[r[i]].push_back(i);
        crs[c[i]].push_back(i);
    }

    for (int i = 0; i < n; ++i) { sort(rcs[i].begin(), rcs[i].end()); }
    for (int i = 0; i < m; ++i) { sort(crs[i].begin(), crs[i].end()); }

    vector<int> comp(cnt, -1);
    int ncomp = 0;
    for (int i = 0; i < cnt; ++i) {
        if (comp[i] == -1) {
            dfs_comp(r, c, rcs, crs, i, ncomp, comp);
            ncomp += 1;
        }
    }

    int emptyr = 0, emptyc = 0;
    for (int i = 0; i < n; ++i) {
        if (rcs[i].empty()) { emptyr += 1; }
    }
    for (int i = 0; i < m; ++i) {
        if (crs[i].empty()) { emptyc += 1; }
    }

    cout << ncomp - 1 + emptyr + emptyc << endl;
}


#include <algorithm>
#include <cstdio>
#include <vector>
#include <cstdint>
using namespace std;

vector<int8_t> usedr, usedc;
vector<int> comp;

vector<int> parent, rk;

void make_set(int v) {
    parent[v] = v;
    rk[v] = 0;
}

int find_set(int v) {
    if (v == parent[v])
        return v;
    return parent[v] = find_set(parent[v]);
}

void union_sets(int a, int b) {
    a = find_set(a);
    b = find_set(b);
    if (a != b) {
        if (rk[a] < rk[b])
            swap(a, b);
        parent[b] = a;
        if (rk[a] == rk[b])
            ++rk[a];
    }
}

int main()
{
    int n, m, cnt;
    scanf("%d%d%d", &n, &m, &cnt);
    usedr.resize(n);
    usedc.resize(m);

    parent.resize(n + m);
    rk.resize(n + m);
    for (int i = 0; i < n + m; ++i) { make_set(i); }

    for (int i = 0; i < cnt; ++i) {
        int r, c;
        scanf("%d%d", &r, &c); --r; --c;
        usedr[r] = 1;
        usedc[c] = 1;
        union_sets(r, n + c);
    }

    vector<int> comps; comps.reserve(n + m);
    for (int i = 0; i < n; ++i) { if (usedr[i]) { comps.push_back(find_set(i)); } }
    sort(comps.begin(), comps.end());
    int ncomp = unique(comps.begin(), comps.end()) - comps.begin();

    int emptyr = count(usedr.begin(), usedr.end(), 0), emptyc = count(usedc.begin(), usedc.end(), 0);

    printf("%d\n", ncomp - 1 + emptyr + emptyc);
}


#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;

vector<int8_t> usedr, usedc;
vector<int> comp;

vector<int> parent, rk;

void make_set(int v) {
    parent[v] = v;
    rk[v] = 0;
}

int find_set(int v) {
    if (v == parent[v])
        return v;
    return parent[v] = find_set(parent[v]);
}

void union_sets(int a, int b) {
    a = find_set(a);
    b = find_set(b);
    if (a != b) {
        if (rk[a] < rk[b])
            swap(a, b);
        parent[b] = a;
        if (rk[a] == rk[b])
            ++rk[a];
    }
}

int main()
{
    int n, m, cnt;
    scanf("%d%d%d", &n, &m, &cnt);
    usedr.resize(n);
    usedc.resize(m);

    parent.resize(n + m);
    rk.resize(n + m);
    for (int i = 0; i < n + m; ++i) { make_set(i); }

    for (int i = 0; i < cnt; ++i) {
        int r, c;
        scanf("%d%d", &r, &c); --r; --c;
        usedr[r] = 1;
        usedc[c] = 1;
        union_sets(r, n + c);
    }

    vector<int> comps; comps.reserve(n + m);
    for (int i = 0; i < n; ++i) { if (usedr[i]) { comps.push_back(find_set(i)); } }
    sort(comps.begin(), comps.end());
    int ncomp = unique(comps.begin(), comps.end()) - comps.begin();

    int emptyr = count(usedr.begin(), usedr.end(), 0), emptyc = count(usedc.begin(), usedc.end(), 0);

    printf("%d\n", ncomp - 1 + emptyr + emptyc);
}


#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;

void dfs_comp(
    vector<int> const& r, vector<int> const& c, vector<vector<int>> const& rcs, vector<vector<int>> const& crs,
    int v, int ncomp, vector<int>& comp
) {
    comp[v] = ncomp;
    for (int i : rcs[r[v]]) {
        if (comp[i] == -1) {
            dfs_comp(r, c, rcs, crs, i, ncomp, comp);
        }
    }
    for (int i : crs[c[v]]) {
        if (comp[i] == -1) {
            dfs_comp(r, c, rcs, crs, i, ncomp, comp);
        }
    }
}

int main()
{
    int n, m, cnt;
    cin >> n >> m >> cnt;
    vector<int> r(cnt), c(cnt);
    vector<vector<int>> rcs(n), crs(m);
    for (int i = 0; i < cnt; ++i) {
        scanf("%d%d", &r[i], &c[i]); --r[i]; --c[i];
        rcs[r[i]].push_back(i);
        crs[c[i]].push_back(i);
    }

    //for (int i = 0; i < n; ++i) { sort(rcs[i].begin(), rcs[i].end()); }
    //for (int i = 0; i < m; ++i) { sort(crs[i].begin(), crs[i].end()); }

    vector<int> comp(cnt, -1);
    int ncomp = 0;
    for (int i = 0; i < cnt; ++i) {
        if (comp[i] == -1) {
            dfs_comp(r, c, rcs, crs, i, ncomp, comp);
            ncomp += 1;
        }
    }

    int emptyr = 0, emptyc = 0;
    for (int i = 0; i < n; ++i) {
        if (rcs[i].empty()) { emptyr += 1; }
    }
    for (int i = 0; i < m; ++i) {
        if (crs[i].empty()) { emptyc += 1; }
    }

    cout << ncomp - 1 + emptyr + emptyc << endl;
}