zoukankan      html  css  js  c++  java
  • 7246. 【USACO 2021 January Contest, Gold】 Dance Mooves

    Description&Data Constraint

    (1le Kle 2 imes 10^5)

    Solution

    首先建议去做一下银组的弱化版:P7299 [USACO21JAN] Dance Mooves S

    银组的做法是:

    记录 (K)​ 次交换后每个点的位置,初末位置连边,则会形成一些环。

    由于时间无限,则环上的每个点的答案是一样的,因此可以开个 ( ext{vector}) 记录每个点走过的位置,最后开个 ( ext{set}) 维护即可。

    这两道题的区别在于金组的这道题目增加了时间的限制,这就意味着有些环是无法走完的。

    先求出在时间限制下 (K) 次换位完整的进行了多少轮,记为 (times),同时求出还剩下多少次换位 (other)

    按照银组的做法先将环弄出来,记录每个环上有哪些点。

    对于环的大小小于等于 (times) 的,做法同银组,因为这些环是能够在 (times)(K)​ 次换位后,每个点至少回到原点一次,所以这个环上的答案都是一样的。(可能有点绕)

    那么现在就思考那些环的大小大于 (times) 的。

    思考在这些环上的点是怎么移动的。

    对于一个点 (i),它在 (K) 次换位后到达的点记为 (nxt_i),那么这次换位中 (i) 所经过的点可以用个 ( ext{vector}) 记录下来,然后对于 (times)(K),每次就可以记录下从 (i)(nxt_i) 中经过的点,然后再从 (times) 轮后的位置再往下走 (other)​ 次换位,最后统计答案就好。

    但是这个方法的时间复杂度十分的高,达到了 (mathcal O(NK))。​

    那么优化就可以考虑破环成链,将环复制一次,开个双指针,每次移动前删除当前 (l)​​ 的贡献和 (r+1)​​ 的剩下贡献。然后移动,加入新的 (r)​​ 的贡献和 (r+1)​​ 的剩下贡献。(注意贡献和剩下贡献是不一样的)(其实就是滑动窗口维护)。

    最后把贡献挂在 (l) 上就可以了。

    Code

    #include<cstdio>
    #include<vector>
    #include<cstring>
    #define ll long long
    #define N 200005
    using namespace std;
    vector<int> spot[N],rest[N],ring[N];
    int n,num,a[N],b[N],pos[N],fro[N],ans[N],buck[N],len[N];
    ll m,k,times,other;
    bool isin[N];
    void getring(int x)
    {
    	int now=x;
    	do
    	{
    		isin[now]=true;
    		ring[num].push_back(now);
    		now=fro[now];
    	}while (now!=x);
    	len[num]=ring[num].size();
    }
    void calcring(int now)
    {
    	int res=0;
    	if ((ll)len[now]<=times)
    	{
    		for (int i=0;i<len[now];++i)
    		{
    			int x=ring[now][i];
    			for (int j=0;j<spot[x].size();++j)
    			{
    				int y=spot[x][j];
    				if (!buck[y]++) ++res;
    			}
    		}
    		for (int i=0;i<len[now];++i)
    			ans[ring[now][i]]=res;
    		for (int i=0;i<len[now];++i)
    		{
    			int x=ring[now][i];
    			for (int j=0;j<spot[x].size();++j)
    			{
    				int y=spot[x][j];
    				buck[y]=0;
    			}
    		}
    		return;
    	}
    	for (int i=0;i<len[now];++i)
    		ring[now].push_back(ring[now][i]);
    	for (int i=0;i<times;++i)
    	{
    		int x=ring[now][i];
    		for (int j=0;j<spot[x].size();++j)
    		{
    			int y=spot[x][j];
    			if (!buck[y]++) ++res;
    		}
    	}
    	for (int i=0;i<rest[ring[now][times]].size();++i)
    	{
    		int x=rest[ring[now][times]][i]; 
    		if (!buck[x]) ++res;
    		++buck[x];
    	}
    	ans[ring[now][0]]=res;
    	int l=0,r=times-1;
    	while (l<len[now]-1)
    	{
    		int x=ring[now][l];
    		if (l<=r)
    		{
    			for (int i=0;i<spot[x].size();++i)
    			{
    				int y=spot[x][i];
    				if (buck[y]==1) --res;
    				--buck[y];
    			}
    		}
    		x=ring[now][r+1];
    		for (int i=0;i<rest[x].size();++i)
    		{
    			int y=rest[x][i];
    			if (buck[y]==1) --res;
    			--buck[y];
    		}
    		++l;++r;
    		x=ring[now][r];
    		if (l<=r)
    		{
    			for (int i=0;i<spot[x].size();++i)
    			{
    				int y=spot[x][i];
    				if (!buck[y]++) ++res;
    			}
    		}
    		x=ring[now][r+1];
    		for (int i=0;i<rest[x].size();++i)
    		{
    			int y=rest[x][i];
    			if (!buck[y]++) ++res;
    		}
    		ans[ring[now][l]]=res;
    	}
    	for (int i=l;i<=r;++i)
    	{
    		int x=ring[now][i];
    		for (int j=0;j<spot[x].size();++j)
    			buck[spot[x][j]]=0;
    	}
    	for (int i=0;i<rest[ring[now][r+1]].size();++i)
    	{
    		int x=rest[ring[now][r+1]][i];
    		buck[x]=0;
    	}
    	memset(buck,0,sizeof(buck));
    }
    int main()
    {
    	scanf("%d%lld%lld",&n,&k,&m);
    	times=m/k;other=m%k;
    	for (int i=1;i<=k;++i)
    		scanf("%d%d",&a[i],&b[i]);
    	for (int i=1;i<=n;++i)
    		spot[i].push_back(i),rest[i].push_back(i),pos[i]=i;
    	for (int i=1;i<=k;++i)
    	{
    		spot[pos[a[i]]].push_back(b[i]);
    		spot[pos[b[i]]].push_back(a[i]);
    		if (i<=other)
    		{
    			rest[pos[a[i]]].push_back(b[i]);
    			rest[pos[b[i]]].push_back(a[i]);
    		}
    		swap(pos[a[i]],pos[b[i]]);
    	}
    	for (int i=1;i<=n;++i)
    	{
    		fro[pos[i]]=i;
    		if (spot[i].size()!=1)	spot[i].pop_back();
    	}
    	for (int i=1;i<=n;++i)
    		if (!isin[i]) ++num,getring(i);
    	for (int i=1;i<=num;++i)
    		calcring(i);
    	for (int i=1;i<=n;++i)
    		printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    VIM配置
    VSCode配置Import@路径
    Sar
    VIM-Fold折叠
    sysctl
    java8 到 java14新增的特性
    Electron整合VUE
    使用Markfile开发GO程序
    cron 表达式
    java spi
  • 原文地址:https://www.cnblogs.com/Livingston/p/15159055.html
Copyright © 2011-2022 走看看