2023 Hubei Provincial Collegiate Programming Contest(gym104337)A. Prime Magic

題目大意

給出一個數列ai,每次可以選擇一個區間[l,r]進行全體+1全體-1,需要滿足區間長度len=r-l+1爲奇質數p,且操作過程中ai非負

求最少操作次數使得最終ai不減

n<=2e3,1<=ai<=1e5,Σn<=2e4

題解

ai不減顯然要差分,設差分得到b[i],使得最終b[i]>=0;設a[n+1]=b[n]=+inf,a[0]=0,b[0]=a[1]

發現原操作等於選擇一個奇質數p,對b[i]+x,b[i+p]-x

注意p是奇質數一開始沒有注意,所以i和i+p奇偶性不同,每次選擇一對來對bi,bi+p操作(將一個+1移到-1上抵消)

按照正負性b[i]的位置i分成二分圖,正連向負,考慮 從正x連向負y的 邊權:
①奇偶異號,且差距爲質數p:代價1
②奇偶同號:根據哥德巴赫猜想,可以用兩個奇質數解決(2=5-3,4=7-3,也可以解決),代價2
③奇偶異號,且差距不爲質數:一個奇質數+哥猜的兩個,代價3

連邊跑費用流,不知道能不能過

考慮先跑一部分邊,跑完之後再加剩下的邊跑:
先只加①邊,跑費用流(實際是二分圖匹配),複雜度\(O(m\sqrt n)\)

發現此時獨立爲兩個二分圖,四個部分跑完之後的剩餘個數都是確定
直接貪心匹配剩下的就好了,優先匹配奇偶同號,剩下的奇偶異號匹配就行了

最後複雜度只取決於①部分,實際m=質數個數*n=\(\frac{n}{\ln n}*n=\frac{n^2}{\ln n}\)

所以總複雜度爲\(O(\frac{n^2\sqrt n}{\ln n})\)

code

ozy寫的

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2005;
const int inf=1e9+7;
int n;
int a[N],b[N];

struct qq{int x,y,z,last;}e[N*N*2];
int num,last[N];
int st,ed;
void init (int x,int y,int z)
{
	if (z==0) return ;
//	printf("%d %d %d\n",x,y,z);
    num++;
    e[num].x=x;e[num].y=y;e[num].z=z;
    e[num].last=last[x];
    last[x]=num;
    swap(x,y);z=0;
    num++;
    e[num].x=x;e[num].y=y;e[num].z=z;
    e[num].last=last[x];
    last[x]=num;
}
int h[N];
bool Bt ()
{
    memset(h,-1,sizeof(h));h[st]=1;
    queue<int> q;
    q.push(st);
    while (!q.empty())
    {
        int x=q.front();q.pop();
        for (int u=last[x];u!=-1;u=e[u].last)
        {
            int y=e[u].y;
            if (e[u].z>0&&h[y]==-1)
            {
                h[y]=h[x]+1;
                q.push(y);
            }
        }
    }
    return h[ed]!=-1;
}
int dfs (int x,int f)
{
    if (x==ed) return f;
    int s1=0;
    for (int u=last[x];u!=-1;u=e[u].last)
    {
        int y=e[u].y;
        if (e[u].z>0&&h[y]==h[x]+1&&s1<f)
        {
            int lalal=dfs(y,min(e[u].z,f-s1));
            s1+=lalal;
            e[u].z-=lalal;
            e[u^1].z+=lalal;
        }
    }
    if (s1==0) h[x]=-1;
    return s1;
}
vector<int> pri;
bool check (int x)
{
	for (int u=2;u*u<=x;u++)
		if (x%u==0)
			return false;
	return true; 
}
int main()
{
	int T;
	scanf("%d",&T);
	while (T--)
	{
		num=1;memset(last,-1,sizeof(last));
		
		scanf("%d",&n);
		pri.clear();
		for (int u=3;u<=n;u++)
		if (check(u))
		pri.push_back(u);
		
		for (int u=1;u<=n;u++) scanf("%d",&b[u]);
		a[1]=b[1];
		for (int u=2;u<=n;u++) a[u]=b[u]-b[u-1];
		a[n+1]=inf;n++;
//		for (int u=1;u<=n;u++) printf("%d ",a[u]);
//		printf("\n");
		st=n+1;ed=st+1;
		for (int u=1;u<=n;u++)
		{
			int tmp=a[u];
			if (tmp<0) tmp=-tmp;
			if (u&1) init(st,u,tmp);
			else init(u,ed,tmp);
			for (auto xx:pri)
			{
				if (u+xx<=n&&(LL)a[u]*a[u+xx]<0)
				{
					if (u&1) init(u,u+xx,inf);
					else 	 init(u+xx,u,inf);
				}
			}
		}
		
		int ans=0;
		while (Bt()) ans=ans+dfs(st,inf);
		
		int pos1=0,neg1=0;
		for (int u=last[st];u!=-1;u=e[u].last)
		{
			int y=e[u].y;
		//	printf("%d %d\n",y,a[y]);
			if (a[y]>=0)	pos1=pos1+e[u].z;
			else neg1=neg1+e[u].z;
		}
		int pos2=0,neg2=0;
		for (int u=last[ed];u!=-1;u=e[u].last)
		{
			int y=e[u].y;
			if (a[y]>=0)	pos2=pos2+a[y]-e[u].z;
			else neg2=neg2+(-a[y])-e[u].z;
		}
		
		//printf("%d %d %d %d %d\n",ans,neg1,pos1,neg2,pos2);
		if (pos1>=neg1) ans=ans+2*neg1;
		else ans=ans+2*pos1+3*(neg1-pos1);
		
		if (pos2>=neg2) ans=ans+2*neg2;
		else ans=ans+2*pos2+3*(neg2-pos2);
		
		printf("%d\n",ans);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章