• XVIII Open Cup named after E.V. Pankratiev. Ukrainian Grand Prix


    A. Accommodation Plan

    对于已知的$K$个点,离它们距离都不超过$L$的点在树上是一个连通块,考虑在每种方案对应的离$1$最近的点统计。

    即对于每个点$x$,统计离它距离不超过$L$的点数$call[x]$,再减去离它和它父亲距离都不超过$L$的点数$cext[x]$,然后用组合数计算方案数。

    对于$call[x]$可以通过点分治+排序双指针$O(nlog^2n)$统计。

    对于$cext[x]$,注意到$cext[x]=call[x]-csub[x]$,其中$csub[x]$表示$x$点子树中深度在$(dep[x]+L-dis(x,fa[x]),dep[x]+L]$的点数,二维数点问题,扫描线+树状数组维护即可$O(nlog n)$统计。

    时间复杂度$O(nlog^2n)$。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=100010,P=1000000007;
    int n,K,L,i,j,x,y,z,fac[N],inv[N],ans;
    int g[N],nxt[N<<1],v[N<<1],w[N<<1],ok[N<<1],ed,son[N],f[N],all,now;
    int call[N],csub[N],cnt,bit[N],st[N],en[N],dfn;
    struct E{ll x;int y;E(){}E(ll _x,int _y){x=_x,y=_y;}}e[N],q[N<<1];
    inline bool cmp(const E&a,const E&b){return a.x<b.x;}
    inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;ok[ed]=1;}
    void findroot(int x,int y){
      son[x]=1,f[x]=0;
      for(int i=g[x];i;i=nxt[i])if(v[i]!=y&&ok[i]){
        findroot(v[i],x);
        son[x]+=son[v[i]];
        if(son[v[i]]>f[x])f[x]=son[v[i]];
      }
      if(all-son[x]>f[x])f[x]=all-son[x];
      if(f[x]<f[now])now=x;
    }
    void dfs(int x,int y,ll z){
      e[++cnt]=E(z,x);
      for(int i=g[x];i;i=nxt[i])if(v[i]!=y&&ok[i])dfs(v[i],x,z+w[i]);
    }
    inline void cal(int p){
      sort(e+1,e+cnt+1,cmp);
      int i,j;
      for(i=1,j=cnt;i<=cnt;i++){
        while(j&&e[i].x+e[j].x>L)j--;
        call[e[i].y]+=p*j;
      }
    }
    void solve(int x){
      int i;
      cnt=0;
      dfs(x,0,0);
      cal(1);
      for(i=g[x];i;i=nxt[i])if(ok[i]){
        cnt=0;
        dfs(v[i],x,w[i]);
        cal(-1);
      }
      for(i=g[x];i;i=nxt[i])if(ok[i]){
        ok[i^1]=0;
        f[0]=all=son[v[i]];
        findroot(v[i],now=0);
        solve(now);
      }
    }
    void dfs2(int x,int y,ll z){
      st[x]=++dfn;
      e[x]=E(z,x);
      q[++cnt]=E(z+L,x);
      for(int i=g[x];i;i=nxt[i])if(v[i]!=y){
        q[++cnt]=E(z+L,-v[i]);
        dfs2(v[i],x,z+w[i]);
      }
      en[x]=dfn;
    }
    inline void ins(int x){for(;x<=n;x+=x&-x)bit[x]++;}
    inline int ask(int x){int t=0;for(;x;x-=x&-x)t+=bit[x];return t;}
    inline int C(int n,int m){return n<m?0:1LL*fac[n]*inv[m]%P*inv[n-m]%P;}
    int main(){
      scanf("%d%d%d",&n,&K,&L);
      for(ed=i=1;i<n;i++)scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
      f[0]=all=n;findroot(1,now=0);solve(now);
      dfs2(1,cnt=0,0);
      sort(e+1,e+n+1,cmp);
      sort(q+1,q+cnt+1,cmp);
      for(i=j=1;i<=cnt;i++){
        while(j<=n&&e[j].x<=q[i].x)ins(st[e[j++].y]);
        x=q[i].y;
        if(x<0)x=-x;
        y=ask(en[x])-ask(st[x]-1);
        if(q[i].y>0)csub[x]+=y;else csub[x]-=y;
      }
      for(fac[0]=i=1;i<=n;i++)fac[i]=1LL*fac[i-1]*i%P;
      for(inv[0]=inv[1]=1,i=2;i<=n;i++)inv[i]=1LL*(P-inv[P%i])*(P/i)%P;
      for(i=2;i<=n;i++)inv[i]=1LL*inv[i-1]*inv[i]%P;
      for(i=1;i<=n;i++)ans=((ans+C(call[i],K))%P+P-C(call[i]-csub[i],K))%P;
      ans=1LL*ans*fac[K]%P;
      printf("%d",ans);
    }
    

      

    B. Card Game

    枚举$n$的所有约数判断。

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<string>
    #include<ctype.h>
    #include<math.h>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    #include<bitset>
    #include<algorithm>
    #include<time.h>
    using namespace std;
    void fre() {  }
    #define MS(x, y) memset(x, y, sizeof(x))
    #define ls o<<1
    #define rs o<<1|1
    typedef long long LL;
    typedef unsigned long long UL;
    typedef unsigned int UI;
    template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
    template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
    const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
    template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
    int casenum, casei;
    int n, m;
    int a[N];
    map<int, int> mop;
    
    int main()
    {
        while(~ scanf("%d%d", &n, &m)){
            mop.clear();
            int A = 0, B = 0;
            for(int i = 1; i <= m; i ++){
                scanf("%d", &a[i]);
                gmax(A, a[i]);
                mop[a[i]] ++;
                gmax(B, mop[a[i]]);
            }
            //printf("%d %d
    ", A, B);
            int ans = 0;
            for(int i = 1; i * i <= n; i ++){
                if(n % i == 0){
                    int j = n / i;
                    if(i >= A && j >= B){
                        ans ++;
                        //printf("i j %d %d
    ", i, j);
                    }
                    if(i != j && i >= B && j >= A){
                        ans ++;
    
                        //printf("j i %d %d
    ", j, i);
                    }
                }
            }
            if(ans != 1) puts("NO");
            else puts("YES");
        }
    	return 0;
    }
    
    /*
    【trick&&吐槽】
    
    
    【题意】
    
    
    【分析】
    
    
    【时间复杂度&&优化】
    
    
    */
    

      

    C. The Most Expensive Gift

    若循环节长度为$1$,则答案下界为$frac{n^2}{9}$,经过分析可得循环节长度超过$8$都是不优的。

    暴力搜索所有长度不超过$8$的串作为循环节,然后贪心匹配计算长度即可。

    时间复杂度$O(3^8n)$。

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<string>
    #include<ctype.h>
    #include<math.h>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    #include<bitset>
    #include<algorithm>
    #include<time.h>
    using namespace std;
    void fre() {  }
    #define MS(x, y) memset(x, y, sizeof(x))
    #define ls o<<1
    #define rs o<<1|1
    typedef long long LL;
    typedef unsigned long long UL;
    typedef unsigned int UI;
    template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
    template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
    const int N = 10010, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
    template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
    int casenum, casei;
    int n;
    char s[N];
    char a[N];
    int ans;
    int len;
    int check()
    {
    	int pos = 0;
    	int now = 0;
    	for(int i = 0; i < n; ++i)
    	{
    		if(s[i] == a[pos])
    		{
    			++now;
    			pos = pos + 1;
    			if(pos == len)pos = 0;
    		}
    	}
    	int LEN = now / len * len;
    	return LEN * LEN / len;
    }
    int main()
    {
    	while(~scanf("%d%s",&n, s))
    	{
    		ans = 0;
    		for(len = 1; ; ++len)
    		{
    			int bst = n * n / len;
    			if(bst <= ans)break;
    			
    			int top = 1;
    			for(int i = 0; i < len; ++i)
    			{
    				a[i] = 'a';
    				top *= 3;
    			}
    			for(int tim = 1; tim <= top; ++tim)
    			{
    				gmax(ans, check());
    				++a[len - 1];
    				int pos = len - 1;
    				while(a[pos] == 'd')
    				{
    					a[pos] = 'a';
    					++a[--pos];
    				}
    			}
    		}
    		printf("%d
    ", ans);
    	}
    	return 0;
    }
    
    /*
    【trick&&吐槽】
    
    
    【题意】
    
    
    【分析】
    
    
    【时间复杂度&&优化】
    
    
    */
    

      

    D. Cut the Cake

    两个维度独立,对于一维问题,将所有位置排序,每隔$k$个切一刀即可。

    #include<cstdio>
    const int N=300;
    int n,m,k,i,j,dx,dy,cntx,cnty,cx[N],cy[N],qx[N],qy[N];char a[N][N];
    inline int cal(int xl,int xr,int yl,int yr){
    	int t=0;
    	for(int i=xl;i<=xr;i++)for(int j=yl;j<=yr;j++)if(a[i][j]=='1')t++;
    	return t;
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&k);
    	for(i=1;i<=n;i++)scanf("%s",a[i]+1);
    	for(i=1;i<=n;i++)for(j=1;j<=m;j++)if(a[i][j]=='1'){
    		dx++;
    		if(dx%k==0&&dx<k*k)cx[i]=1;
    	}
    	for(j=1;j<=m;j++)for(i=1;i<=n;i++)if(a[i][j]=='1'){
    		dy++;
    		if(dy%k==0&&dy<k*k)cy[j]=1;
    	}
    	for(i=1;i<n;i++)if(cx[i])qx[++cntx]=i;
    	for(i=1;i<m;i++)if(cy[i])qy[++cnty]=i;
    	if(cntx!=k-1||cnty!=k-1)return puts("NO"),0;
    	qx[cntx+1]=n;
    	qy[cnty+1]=m;
    	for(i=1;i<=cntx;i++)for(j=1;j<=cnty;j++)if(cal(qx[i-1]+1,qx[i],qy[j-1]+1,qy[j])!=1)return puts("NO"),0;
    	puts("YES");
    	for(i=1;i<=cntx;i++)printf("%d ",qx[i]);puts("");
    	for(i=1;i<=cnty;i++)printf("%d ",qy[i]);puts("");
    }
    

      

    E. Message

    留坑。

    F. Bad Word

    若整个串不是回文串,则答案显然为$1$。

    若整个串形如$aaaaa$、$aabaa$、$ababa$,则无解。

    否则答案必然是$2$。

    #include<cstdio>
    const int N=200010;
    int n,i;char a[N];
    int main(){
    	scanf("%d%s",&n,a+1);
    	for(i=1;i<=n;i++)if(a[i]!=a[n-i+1])break;
    	if(i<=n)return puts("1"),0;
    	for(i=1;i<=n;i++)if(a[i]!=a[1])break;
    	if(i>n)return puts("-1"),0;
    	if(n%2==0)return puts("2"),0;
    	for(i=1;i<=n/2;i++)if(a[i]!=a[1])break;
    	if(i>n/2)return puts("-1"),0;
    	for(i=1;i<=n;i++){
    		if(i>2&&a[i]!=a[i-2])break;
    	}
    	if(i>n)return puts("-1"),0;
    	puts("2");
    }
    

      

    G. Zenyk, Marichka and Interesting Game

    首先将所有石子数都模$A+B$,并剔除小于$min(A,B)$的堆。

    若$A=B$,则根据奇偶性判断即可。

    若$A>B$,枚举$A$的第一步行动,然后交换先后手,转化为$A<B$的问题。

    若$A<B$:

    • 若$n=0$,则先手必败。
    • 若一步之内可以造出只能$A$拿的堆,即存在石子数$<B$或者$geq 2A$的堆,那么$A$可以利用这一堆来控制先后手,从而必胜。
    • 否则任何一堆两人都只能恰好拿一次,故根据$n$的奇偶性判断即可。
    #include<cstdio>
    #include<set>
    #include<algorithm>
    using namespace std;
    typedef pair<int,int>P;
    const int N=100010;
    int n,m,A,B,x,a[N];set<P>T;
    int work(int A,int B){
    	if(T.size()==0)return 0;
    	set<P>::iterator x=T.begin();
    	if(x->first<B)return 1;
    	x=T.end();
    	x--;
    	if(x->first>=A*2)return 1;
    	return T.size()%2;
    }
    int solve(){
    	int i,ans=0;
    	if(A==B){
    		for(i=1;i<=m;i++)ans=(ans+a[i]/A)%2;
    		return ans;
    	}
    	if(A<B)return work(A,B);
    	for(i=1;i<=m;i++)if(a[i]>=A){
    		T.erase(P(a[i],i));
    		int t=a[i]-A;
    		if(t<A&&t<B)t=0;
    		if(t)T.insert(P(t,i));
    		if(!work(B,A))return 1;
    		if(t)T.erase(P(t,i));
    		T.insert(P(a[i],i));
    	}
    	return 0;
    }
    int main(){
    	scanf("%d%d%d",&n,&A,&B);
    	while(n--){
    		scanf("%d",&x);
    		x%=A+B;
    		if(x<A&&x<B)continue;
    		a[++m]=x;
    		T.insert(P(x,m));
    	}
    	puts(solve()?"Zenyk":"Marichka");
    }
    

      

    H. Frog Jumping

    贪心用代价最小的若干个青蛙去贪心地走路线。

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<string>
    #include<ctype.h>
    #include<math.h>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    #include<bitset>
    #include<algorithm>
    #include<time.h>
    using namespace std;
    void fre() {  }
    #define MS(x, y) memset(x, y, sizeof(x))
    #define ls o<<1
    #define rs o<<1|1
    typedef long long LL;
    typedef unsigned long long UL;
    typedef unsigned int UI;
    template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
    template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
    const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
    template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
    int casenum, casei;
    int n, m, K, D;
    int c[N];
    LL sum[N];
    int a[N];
    int main()
    {
    	while(~scanf("%d%d%d%d",&n, &m, &K, &D))
    	{
    		for(int i = 1; i <= m; ++i)scanf("%d", &c[i]);
    		sort(c + 1, c + m + 1);
    		for(int i = 1; i <= m; ++i)
    		{
    			sum[i] = sum[i - 1] + c[i];
    		}
    		set<int>sot;
    		for(int i = 1; i <= K; ++i)
    		{
    			scanf("%d", &a[i]);
    			sot.insert(a[i]);
    		}
    		sort(a + 1, a + K + 1);
    		int way = 0;
    		int pos = 1;
    		sot.insert(n);
    		while(!sot.empty())
    		{
    			auto it = sot.upper_bound(pos + D);
    			
    			//can't reach it
    			if(it == sot.begin())
    			{
    				break;
    			}
    			
    			--it;
    			pos = *it;
    			//
    			//printf("pos = %d
    ", pos);
    			//
    			if(pos == n)
    			{
    				++way;
    				if(way >= m)break;
    				pos = 1;
    			}
    			else sot.erase(it);
    		}
    		//
    		//printf("Here: way = %d
    ", way);
    		//
    		LL ans = 0;
    		if(way == 0)
    		{
    			a[0] = 1; a[K + 1] = n;
    			for(int i = 1; i <= K + 1; ++i)
    			{
    				int dis = a[i] - a[i - 1];
    				if(dis > D)
    				{
    					ans += c[1];
    				}
    			}
    			ans += sum[m] - sum[1];
    		}
    		else
    		{
    			ans = sum[m - way];
    		}
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    
    /*
    【trick&&吐槽】
    10 2 3 3
    4 7
    4 8 7
    
    10 2 2 3
    4 7
    9 5
    
    【题意】
    
    
    【分析】
    
    
    【时间复杂度&&优化】
    
    
    */
    

      

    I. Slot Machine

    按照最坏情况模拟即可。

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<string>
    #include<ctype.h>
    #include<math.h>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    #include<bitset>
    #include<algorithm>
    #include<time.h>
    using namespace std;
    void fre() {  }
    #define MS(x, y) memset(x, y, sizeof(x))
    #define ls o<<1
    #define rs o<<1|1
    typedef long long LL;
    typedef unsigned long long UL;
    typedef unsigned int UI;
    template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
    template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
    const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
    template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
    int casenum, casei;
    int n;
    vector<int>vt[N];
    int a[N];
    set< pair<int,int> >sot[N];
    int main()
    {
    	while(~scanf("%d",&n))
    	{
    		int ANS = inf;
    		for(int i = 0; i < N; ++i)sot[i].clear();
    		for(int i = 1; i <= n; ++i)
    		{
    			vt[i].clear();
    			int g;
    			scanf("%d", &g);
    			for(int j = 1; j <= g; ++j)
    			{
    				int x;
    				scanf("%d", &x);
    				vt[i].push_back(x);
    			}
    			sort(vt[i].begin(), vt[i].end());
    			int col = 0;
    			for(int j = 0; j < g; ++j)
    			{
    				if(j == g - 1 || vt[i][j] != vt[i][j + 1])
    				{
    					++col;
    				}
    			}
    			int cnt = 0;
    			for(int j = 0; j < g; ++j)
    			{
    				++cnt;
    				if(j == g - 1 || vt[i][j] != vt[i][j + 1])
    				{
    				    if(cnt > 1)gmin(ANS, col + 1);
    					sot[ vt[i][j] ].insert( {col, i} );
    					cnt = 0;
    				}
    			}
    		}
    		for(int i = 1; i <= n; ++i)
    		{
    			int g = vt[i].size();
    			int cnt = 0;
    			int col = 0;
    			for(int j = 0; j < g; ++j)
    			{
    				++cnt;
    				if(j == g - 1 || vt[i][j] != vt[i][j + 1])
    				{
    					a[++col] = inf;
    					for(auto it : sot[ vt[i][j] ])
    					{
    						int bl = it.second;
    						if(bl == i && cnt == 1)continue;
    						a[col] = it.first + 1;
    						break;
    					}
    					cnt = 0;
    				}
    			}
    			sort(a + 1, a + col + 1);
    			reverse(a + 1, a + col + 1);
    			for(int j = 1; j <= col; ++j)
    			{
    				//printf("(%d %d): %d
    ", i, j, a[j]);
    				gmin(ANS, a[j] + j - 1);
    			}
    		}
    		printf("%d
    ", ANS);
    	}
    	return 0;
    }
    
    /*
    【trick&&吐槽】
    7
    4 1 2 3 4
    1 1
    1 2
    1 3
    1 4
    7 4 7 4 4 7 7 4
    1 5
    
    
    5
    5 1 2 5 3 4
    5 1 2 5 6 7
    5 1 2 5 8 9
    5 1 2 5 10 11
    20 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
    
    2
    5 1 2 3 4 5
    1 1
    
    
    1
    5 1 2 3 1 2
    
    【题意】
    
    
    【分析】
    
    
    【时间复杂度&&优化】
    
    
    */
    

      

    J. Half is Good

    注意到边权在$[0,2^{62})$内随机,故最后$8$位影响排序结果的概率非常小,可以忽略。

    还剩$54$位,分成最高的$22$位和后面$32$位,将最高的$22$位相同的边分组,每组内部排序,每组数量都不多。

    然后直接求最小生成树即可。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef unsigned int U;
    typedef unsigned long long ll;
    const int N=10000010,M=(1<<22)+5;
    int n,m,i,u,v,f[N],have,goal;
    int g[M],nxt[N],q[1000000];
    U e[N][3],ans[N/32+10];
    U s;
    ll w;
    inline bool cmp(int x,int y){return e[x][2]<e[y][2];}
    inline U getnxt(){
    s ^= s << 13;
    s ^= s >> 17;
    s ^= s << 5;
    return s;
    }
    int F(int x){return f[x]==x?x:f[x]=F(f[x]);}
    inline void merge(int x){
    	int a=e[x][0],b=e[x][1];
    	//printf("merge %d %d
    ",a,b);
    	if(F(a)!=F(b)){
    		have++;
    		ans[x>>5]|=1U<<(x&31);
    		f[f[a]]=f[b];
    	}
    }
    inline void solve(int x){
    	if(g[x]<0)return;
    	if(nxt[g[x]]<0){
    		merge(g[x]);
    		return;
    	}
    	int cnt=0;
    	for(int i=g[x];~i;i=nxt[i])q[cnt++]=i;
    	sort(q,q+cnt,cmp);
    	for(int i=0;i<cnt;i++)merge(q[i]);
    }
    int main(){
    	scanf("%d%d%u",&n,&m,&s);
    	goal=(n-1)/2+1;
    	goal=min(goal,n-1);
    	U mask=~0U;
    	for(i=0;i<M;i++)g[i]=-1;
    	for(i=0;i<m;i++){
    		u=getnxt()%n;
    		v=getnxt()%n;
    		w=getnxt()>>2;
    		w=w*getnxt();
    		w>>=8;
    		e[i][0]=u;
    		e[i][1]=v;
    		e[i][2]=w&mask;
    		w>>=32;
    		nxt[i]=g[w];
    		g[w]=i;
    		//printf("%d %d %llu
    ",u,v,w);
    	}
    	for(i=0;i<n;i++)f[i]=i;
    	for(i=0;i<M&&have<goal;i++)solve(i);
    	m=(m-1)/32;
    	for(i=0;i<=m;i++)printf("%u ",ans[i]);
    }
    

      

    K. Dance

    留坑。

    L. Impress Her

    对于一个$A imes B$的包围矩形,因为四连通,至少消耗$A+B$个点,故直接枚举每个矩形内部所有点的时间复杂度为$O(n^3)$。

    对于每个点记录其所属矩形,对于每个矩形$x$,枚举内部所有点,检查$x$是否包含该点所属矩形即可,用时间戳来$O(1)$判重。

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<string>
    #include<ctype.h>
    #include<math.h>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    #include<bitset>
    #include<algorithm>
    #include<time.h>
    using namespace std;
    void fre() {  }
    #define MS(x, y) memset(x, y, sizeof(x))
    #define ls o<<1
    #define rs o<<1|1
    typedef long long LL;
    typedef unsigned long long UL;
    typedef unsigned int UI;
    template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
    template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
    const int N = 505, M = 1e6 + 10, Z = 1e9 + 7, inf = 0x3f3f3f3f;
    template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
    int casenum, casei;
    int n, m;
    int a[N][N];
    bool vis[M];
    bool vs[N][N];
    int dfn[M];
    int ymin[M];
    int ymax[M];
    int xmin[M];
    int xmax[M];
    
    const int dy[4] = {-1,0,0,1};
    const int dx[4] = {0,-1,1,0};
    void dfs(int y, int x)
    {
    	if(vs[y][x])return;
    	vs[y][x] = 1;
    	gmin(ymin[a[y][x]], y);
    	gmax(ymax[a[y][x]], y);
    	gmin(xmin[a[y][x]], x);
    	gmax(xmax[a[y][x]], x);
    	for(int k = 0; k < 4; ++k)
    	{
    		int yy = y + dy[k];
    		int xx = x + dx[k];
    		if(a[yy][xx] == a[y][x])
    		{
    			dfs(yy, xx);
    		}
    	}
    }
    int main()
    {
    	while(~scanf("%d%d",&n, &m))
    	{
    		for(int i = 1; i <= n; ++i)
    		{
    			for(int j = 1; j <= m; ++j)
    			{
    				scanf("%d", &a[i][j]);
    			}
    		}
    		MS(vis, 0);
    		MS(vs,0);
    		MS(ymin, 63);
    		MS(ymax, 0);
    		MS(xmin, 63);
    		MS(xmax, 0);
    		for(int i = 1; i <= n; ++i)
    		{
    			for(int j = 1; j <= m; ++j)
    			{
    				if(!vis[ a[i][j] ])
    				{
    					vis[ a[i][j] ] = 1;
    					dfs(i, j);
    				}
    			}
    		}
    		MS(dfn, 0);
    		int tim = 0;
    		int ans = 0;
    		for(int i = 1; i <= n; ++i)
    		{
    			for(int j = 1; j <= m; ++j)
    			{
    				int col = a[i][j];
    				if(vis[col])
    				{
    					vis[col] = 0;
    					++tim;
    					for(int y = ymin[col]; y <= ymax[col]; ++y)
    					{
    						for(int x = xmin[col]; x <= xmax[col]; ++x)
    						{
    							int c = a[y][x];
    							if(c != col && dfn[c] != tim
    							 && ymin[c] >= ymin[col]
    							 && ymax[c] <= ymax[col]
    							 && xmin[c] >= xmin[col]
    							 && xmax[c] <= xmax[col]
    							 )
    							{
    								dfn[c] = tim;
    								++ans;
    							}
    						}
    					}
    				}
    			}
    		}
    		printf("%d
    ", ans);
    	}
    	return 0;
    }
    
    /*
    【trick&&吐槽】
    
    
    【题意】
    
    
    【分析】
    
    
    【时间复杂度&&优化】
    
    
    */
    

      

  • 相关阅读:
    [导入]CodeSmith应用(二)
    [导入]WebService开发(一) 如何使用Soap头
    [导入]WebService开发(三)Web Service Software Factory
    [导入]CodeSmith应用(三)
    [导入]WebService开发(二) 如何使用Soap扩展
    [导入]WinForm下的Msn Popup
    [导入]Flex与Dotnet 之 WebService
    POJ 1243 One Person(经典DP)
    汇编的艺术(02)& operator
    数据结构练习(01)把二元查找树转变成排序的双向链表
  • 原文地址:https://www.cnblogs.com/clrs97/p/8547281.html
Copyright © 2020-2023  润新知