2023牛客暑期多校训练营5

B.Circle of Mistery

题意:有一个由n个点组成的图,现在给出每个点的权值,构造一个排列a,将i与a[i]相连,满足至少有一个从节点1出发的环,其上各点权值之和大于等于k,并且使得排列a中的逆序对数量最少,求出最少的逆序对个数。

Solution

我们考虑到要想尽可能减少逆序对个数,那么其它的点连接方式一定是自环

现在考虑1出发的环上的情况

我们假设从l到r上的点选择一部分作为环,那么在环上有逆序对len-1个,例如234561有5个,243561也是5个,这些贡献全部由1完成

考虑不在环上的点,由于需要跳过某些某些点,比如243561中,3是被跳过的点,环的元素只有24561,跳过一个点的贡献为1,所以跳过x个点的贡献是x,总的贡献就是len-1+x,对于l到r以外的点,是没有贡献的,不会产生逆序对

我们发现,要尽可能使得x小,就必须多加入一些点

对于非负整数来说,我们肯定要加入,他们不会使得权值之和小于k,对于负数来说,我们要尽可能多加入,那么就要多选一些大的负数

void solve()
{
    int n,k;cin>>n>>k;
    for(int i=1;i<=n;i++)cin>>a[i];
    int ans=1e18;
    for(int i=1;i<=n;i++)
    {
        int sum=0,cnt=0;
        multiset<int>st;
        for(int j=i;j<=n;j++)
        {
            if(a[j]<0)
            {
                st.insert(a[j]);
            }
            else
            {
                sum+=a[j];
                cnt++;
            }
            if(sum>=k)
            {
                int temp=sum,tempc=cnt;
                for (auto ti = st.rbegin(); ti != st.rend();++ti)
                {
                    int it = *ti;
                    if(it+temp<k)break;
                    temp+=it;
                    tempc++;
                }
                if(tempc)ans=min(ans,j-i+j-i+1-tempc);
            }
        }
    }
    cout<<((ans==1e18)?-1:ans)<<"\n";
}

C.Cheeeeen the Cute Cat

题意:给出一个二分图,求最大匹配

官解看不懂,但是又不能不补,干脆把相关知识补了

按官解说的来

i到j+n有边时,j到i+n没有边,所以可以看成从i到j的一条边

这样就有n个点和n\(\times\)(n-1)/2,这是一张无向完全图

无向完全图可以通过给予方向来转变为有向完全图有向完全图又称为竞赛图

哈密顿回路:经过每个点一次且仅经过一次并回到起点的路径

竞赛图的性质:任何一个竞赛图都有哈密顿回路,并且如果存在环,一定有三元环。

对于一个普通的有向图:如果它有哈密顿回路,则一定有强连通分量

D.Cirno's Perfect Equation Class

题意:给出k,c,n,已知ka+b=c,a>0,b>0,并且b=xc(x>=1),要求求出满足gcd(a,b)>=n的x的个数

Solution

居然是模拟...

void solve()
{
	int k,c,n;cin>>k>>c>>n;
	int ans=0;
	for(int i=1;i*i<=c;i++)
	{
		if(c%i!=0)continue;
		int t=c-c/i;
		if(t%k==0)
		{
			int a=t/=k;
			int b=c/i;
			if(a>0&&b>0&&gcd(a,b)>=n)ans++;
		}
		t=c-i;
		if(t%k==0)
		{
			int a=t/k;
			int b=i;
			if(a>0&&b>0&&gcd(a,b)>=n)ans++;
		}
	}
	cout<<ans<<"\n";
}

E.Red and Blue and Green

题意:构造一个排列,限制在m个不同的区间中区间内的逆序对个数的奇偶性,其中不同的区间要么相互包含要么不相交

Solution

由于区间的性质,我们可以考虑把他们看成一棵树,遍历对树进行dfs

如果当前是根节点,并且限制为奇数,则交换开头元素即可

如果当前不是根节点,我们可以交换r和r+1的位置,因为r一定比后面的小,所以只会对整个区间的逆序对产生影响,而不会对其子区间产生影响,也可以交换l-1和l的位置

int a[N];
struct node
{
	int l,r,op;
	bool operator <(const node &t)const &{
		if(r-l!=t.r-t.l)return r-l<t.r-t.l;
		return l<t.l;
	}
};
vector<node>v;
vector<int>e[N];
int vis[N];
int b[N];
bool cmp(int x,int y)
{
	return v[x].l<v[y].l;
}
void dfs(int x)
{
	if(e[x].size()==0)
	{
		if(v[x].op==1)swap(a[v[x].l],a[v[x].l+1]);
		return;
	}
	sort(e[x].begin(),e[x].end(),cmp);
	int sum=0;
	for(auto it:e[x])
	{
		dfs(it);
		sum+=v[it].op;
	}
	//cout<<sum<<"\n";
	if((sum%2)!=v[x].op)
	{
		if(v[e[x][0]].l==v[x].l)
		{
			swap(a[v[e[x][0]].r],a[v[e[x][0]].r+1]);
		}else
		{
			swap(a[v[e[x][0]].l-1],a[v[e[x][0]].l]);
		}
	}
}



void solve()
{
	int n,m;cin>>n>>m;
	for(int i=1;i<=n;i++)a[i]=i;
	
	for(int i=1;i<=m;i++)
	{
		int x,y,z;cin>>x>>y>>z;
		if(x==y&&z!=0)
		{
			cout<<"-1\n";
			return;
		}
		if(x!=y)v.push_back({x,y,z});
	}
	sort(v.begin(),v.end());

	for(int i=0;i<v.size();i++)
	{
		for(int j=i+1;j<v.size();j++)
		{
			if(v[j].l<=v[i].l&&v[i].r<=v[j].r)
			{
				e[j].push_back(i);
				vis[i]=1;
				break;
			}
		}
	}
	for(int i=0;i<v.size();i++)
	{
		if(!vis[i])dfs(i);
	}
	for(int i=1;i<=n;i++)b[a[i]]=i;
	for(int i=1;i<=n;i++)cout<<b[i]<<" \n"[i==n];
}

G.Go to Play Maimai DX

题意:有一个数组a,只有1,2,3,4,最长的至少有1个1,1个2, 1个3和k个4的区间是多长

Solution

前缀和二分即可

int a[N];
int b[N][5];
int n,k;
bool check(int i,int x)
{
	for(int j=1;j<=3;j++)
	{
		if(b[x][j]-b[i-1][j]<1)return false;
	}
	return (b[x][4]-b[i-1][4]>=k);
}


void solve()
{
	cin>>n>>k;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=4;j++)
		{
			b[i][j]=b[i-1][j];
		}
		b[i][a[i]]++;
	}

	int ans=INF;
	for(int i=1;i<=n;i++)
	{
		int l=i,r=n;
		while(l<r)
		{
			int mid=(l+r)>>1;
			if(check(i,mid))r=mid;
			else l=mid+1;
		}
		if(check(i,l))ans=min(l-i+1,ans);
	}
	cout<<ans<<"\n";
	
}

H.Nazrin the Greeeeeedy Mouse

题意:纳兹琳有m个包,一个比一个大,她家在一条长为n的路上,家在点1,每两个点之间有一个奶酪,每个奶酪有一个大小a和重量b,她如果想从x点到x+1点,要么给奶酪上面打一个洞,要么拿走奶酪,问她总共最多能拿多重的奶酪

Solution

很显然背包问题,但是m很大,考虑到其实m个包一个比一个大,而n又比m小,所以我们只用拿最大的包即可

然后定义dp[i][j]为到用前i个包到第j个点所能获得的奶酪重量之和的最大值

显然

\[dp[i][j]=max(dp[i][j],dp[i-1][k]+f[i][k+1][j]) \]

f[i][j][k]表示第i个包在区间[j,k]能获得的奶酪重量之和的最大值,这个可以预处理得到

通过前缀和处理还可以把第一维给优化掉

int dp[205][2];
int f[205][205][205];
int a[205],b[205];
int sz[N];

void solve()
{
	int n,m;cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i]>>b[i];
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<=n;j++)
		{
			for(int k=200;k>=0;k--)
			{
				f[k][i][j]=f[k][i][j-1];
				if(k>=a[j])f[k][i][j]=max(f[k][i][j],f[k-a[j]][i][j-1]+b[j]);
			}
		}
	}

	
	for(int i=1;i<=m;i++)cin>>sz[i];
	int op=0;
	for(int i=1;i<=n;i++)dp[i][0]=f[sz[max(0LL,m-n)+1]][1][i];
	op^=1;
	for(int i=max(0LL,m-n)+2;i<=m;i++)
	{
		for(int k=1;k<=n;k++)
		{
			dp[k][op]=dp[k][op^1];
			for(int j=k-1;j>=0;j--)
			{
				dp[k][op]=max(dp[k][op],dp[j][op^1]+f[sz[i]][j+1][k]);
			}
		}
		op^=1;
		for(int j=0;j<=200;j++)dp[j][op]=0;
	}
	cout<<dp[n][op^1]<<"\n";
	
}

I.The Yakumo Family

题意:给出一个序列a,求sum

\[sum=\sum_{1<=l_{1}<=r_{1}<l_{2}<=r_{2}<l_{3}<=r_{3}<=n}XOR(l_{1},r_{1})×XOR(l_{2},r_{2})×XOR(l_{3},r_{3}) \]

其中XOR(l,r)表示序列a在区间[l,r]内的异或和

Solution

对于每一个二进制位上的数,其贡献都是独立的,我们把它拆开来看

先处理出前缀异或和

先看\(\sum\)XOR(\(l_1\),\(r_1\)),对于第k位上的数来说,其对答案的贡献为(1<<k)\(\times\)(前面与它第k位不同的数的个数)

再看\(\sum\)XOR(\(l_1\),\(r_1\))\(\times\)\(\sum\)XOR(\(l_2,r_2\)),这上面的第k位的贡献为(1<<k)\(\times\)(前面与它第k位不同的数的个数\(\times\)(这个不同的数的前面的\(\sum\)XOR(\(l_1\),\(r_1\))的对答案的贡献))

最后同理可以求出\(\sum\)XOR(\(l_1\),\(r_1\))\(\times\)\(\sum\)XOR(\(l_2,r_2\))\(\times\)\(\sum\)XOR(\(l_3,r_3\))

void solve()
{
	int n;cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	int res=0;
	for(int i=1;i<=n;i++)
	{
		res^=a[i];
		s[i]=res;
		t[i]=1;
	}
	int sum=0;
	for(int k=1;k<=3;k++)
	{
		for(int j=0;j<=30;j++)
		{
			cnt[0]=1,cnt[1]=0;
			if(k!=1)cnt[0]=0;
			for(int i=1;i<=n;i++)
			{
				ans[i]=(ans[i]+((cnt[((s[i]>>j)&1)^1])<<j)%mod)%mod;
				cnt[(s[i]>>j)&1]=(cnt[(s[i]>>j)&1]+t[i])%mod;
			}
		}
		for(int i=1;i<=n;i++)t[i]=(t[i-1]+ans[i])%mod;
		for(int i=1;i<=n;i++)ans[i]=0;
	}
	cout<<t[n]<<"\n";
}
posted @ 2023-08-02 16:05  HikariFears  阅读(37)  评论(0)    收藏  举报