1 条题解

  • 0
    @ 2025-12-1 18:48:03

    题解

    把原图和碎片看作由若干非空格单元组成的集合,允许水平/垂直翻转、9090^\circ 的倍数旋转。只要三片碎片的变换后单元集合恰好覆盖原图、字符也一一对应且不重叠,即为可行方案。

    预处理与表示

    1. 读入原图,提取所有非 . 单元,记录坐标与字符,并给每个单元编号 id。用 FULL 位集表示全部目标单元。
    2. 对每个碎片生成 8 个方向(4 次旋转 × 是否水平翻转),提取非空单元并归一化到以左上为 (0,0)(0,0)。去重相同朝向。
    3. 为了枚举位置:选该朝向的第一个单元作为锚点,枚举所有原图中字符相同的单元作为锚点对齐位置,平移后检查所有单元是否仍在原图内、字符匹配。若合法,生成一个位集(长度为 |FULL|),并去重后存入该碎片的可行摆放列表。

    位集用 vector<uint64_t> 存储,支持与/或、异或等操作。

    计数

    若用暴力三层循环会超时。利用“先存两片并集,再配第三片”的思路:

    1. 维护哈希表 pairMap:键为两片不重叠摆放的并集位集,值为这种并集出现的次数。
    2. 依次作为第三片枚举碎片 k(顺序 0..K-1):
      • 对于 k 的每个摆放 P_k,计算补集 need = FULL ^ P_k。若 pairMap 中存在键为 need 的项,说明存在两片(索引均小于 k)的并集恰好覆盖剩余部分且与 P_k 不重叠,于是把计数累加进答案。
      • 随后把当前碎片 k 与所有更早碎片 i<k 的摆放两两尝试合并:若不相交,则把它们的并集加入 pairMap 计数。这保证后续更大的 k 查询时,pairMap 里只包含索引小于当前的两片组合,避免重复计数。

    复杂度取决于合法摆放数量。典型数据下摆放数不大,位集操作为 mathcalO(FULL/64)\\mathcal{O}(|FULL|/64),整体可在限时内通过。

    #include <bits/stdc++.h>
    using namespace std;
    
    struct VecHash{
        size_t operator()(const vector<uint64_t>& v)const noexcept{
            size_t h=0; for(uint64_t x:v) h^=x+0x9e3779b97f4a7c15ULL+(h<<6)+(h>>2); return h;
        }
    };
    struct Cell{int r,c;char ch;};
    
    vector<string> rot(const vector<string>& g){int R=g.size(),C=g[0].size();vector<string> r(C,string(R,'.'));for(int i=0;i<R;++i)for(int j=0;j<C;++j)r[j][R-1-i]=g[i][j];return r;}
    vector<string> flip(const vector<string>& g){auto r=g;for(auto& row:r) reverse(row.begin(),row.end());return r;}
    vector<vector<string>> gen(const vector<string>& g){
        vector<vector<string>> m; auto cur=g;
        for(int k=0;k<4;++k){m.push_back(cur);cur=rot(cur);}
        cur=flip(g); for(int k=0;k<4;++k){m.push_back(cur);cur=rot(cur);}
        vector<vector<string>> u; for(auto& x:m){bool same=false;for(auto& y:u) if(y==x){same=true;break;} if(!same) u.push_back(x);} return u;
    }
    vector<Cell> cells(const vector<string>& g){
        int R=g.size(),C=g[0].size(); vector<Cell> v; int mr=R,mc=C;
        for(int i=0;i<R;++i)for(int j=0;j<C;++j)if(g[i][j]!='.'){v.push_back({i,j,g[i][j]});mr=min(mr,i);mc=min(mc,j);}
        for(auto& c:v){c.r-=mr; c.c-=mc;} sort(v.begin(),v.end(),[](const Cell&a,const Cell&b){return a.r!=b.r?a.r<b.r:a.c!=b.c?a.c<b.c:a.ch<b.ch;}); return v;
    }
    
    int main(){
        ios::sync_with_stdio(false); cin.tie(nullptr);
        int K; if(!(cin>>K)) return 0;
        int R0,C0; cin>>R0>>C0; vector<string> tgt(R0); for(auto& s:tgt) cin>>s;
        vector<vector<string>> raw(K); for(int i=0;i<K;++i){int R,C;cin>>R>>C; raw[i].resize(R); for(auto& s:raw[i]) cin>>s;}
    
        auto tcell=cells(tgt); int tot=tcell.size(); if(tot==0){cout<<0<<'\n'; return 0;}
        unordered_map<long long,int> id; vector<char> tch(tot);
        vector<vector<pair<int,int>>> byc(256);
        for(int i=0;i<tot;++i){long long key=((long long)tcell[i].r<<32)^(unsigned long long)tcell[i].c; id[key]=i; tch[i]=tcell[i].ch; byc[(unsigned char)tcell[i].ch].push_back({tcell[i].r,tcell[i].c});}
        int BL=(tot+63)/64; vector<uint64_t> FULL(BL,~0ULL); if(tot%64) FULL.back()=(1ULL<<(tot%64))-1;
    
        vector<vector<vector<uint64_t>>> place(K);
        for(int idx=0;idx<K;++idx){
            auto mats=gen(raw[idx]); unordered_set<vector<uint64_t>,VecHash> seen;
            for(auto& m:mats){
                auto cs=cells(m); if(cs.empty()) continue;
                auto anchor=cs[0]; auto& cand=byc[(unsigned char)anchor.ch];
                for(auto [tr,tc]:cand){
                    int dr=tr-anchor.r, dc=tc-anchor.c; vector<uint64_t> bits(BL,0); bool ok=true;
                    for(auto& c:cs){
                        int nr=c.r+dr,nc=c.c+dc; if(nr<0||nr>=R0||nc<0||nc>=C0){ok=false;break;}
                        long long key=((long long)nr<<32)^(unsigned long long)nc; auto it=id.find(key); if(it==id.end()||tch[it->second]!=c.ch){ok=false;break;}
                        int idv=it->second; bits[idv>>6]|=1ULL<<(idv&63);
                    }
                    if(ok && seen.insert(bits).second) place[idx].push_back(move(bits));
                }
            }
        }
    
        auto disjoint=[](const vector<uint64_t>& a,const vector<uint64_t>& b){for(size_t i=0;i<a.size();++i) if(a[i]&b[i]) return false; return true;};
        auto uni=[](const vector<uint64_t>& a,const vector<uint64_t>& b){vector<uint64_t> r(a.size()); for(size_t i=0;i<a.size();++i) r[i]=a[i]|b[i]; return r;};
        auto comp=[&](const vector<uint64_t>& a){vector<uint64_t> r(a.size()); for(size_t i=0;i<a.size();++i) r[i]=FULL[i]^a[i]; return r;};
    
        long long ans=0; unordered_map<vector<uint64_t>, long long, VecHash> pairMap;
        for(int k=0;k<K;++k){
            for(auto& pk:place[k]){auto need=comp(pk); auto it=pairMap.find(need); if(it!=pairMap.end()) ans+=it->second;}
            for(int i=0;i<k;++i) for(auto& pi:place[i]) for(auto& pk:place[k]) if(disjoint(pi,pk)){auto u=uni(pi,pk); pairMap[u]++;}
        }
        cout<<ans<<'\n';
        return 0;
    }
    
    • 1

    「USACO 2016 US Open, Platinum」Bull in a China Shop

    信息

    ID
    5721
    时间
    1000ms
    内存
    256MiB
    难度
    7
    标签
    递交数
    4
    已通过
    1
    上传者