1917题目&题解

CF660D Number of Parallelograms

题意

题目描述

给定平面上\(n\)个点,问用这些点能组成多少平行四边形(多个平行四边形可以共用点,保证任意三点不共线)

数据范围

\(1 \leq n \leq 2000\) , \(0\leq x_i,y_i \leq 10^{9}\)

题解

平四对角线互相平分,所以 \(n^2\) 求出两两之间的线段中点,如果有 \(k\) 条线段的中点在 \((x,y)\),那么说明存在 \(C_k^2\) 个平四。

代码

#include<bits/stdc++.h>
using namespace std;
int n,ans;
map<pair<int,int>,int>mp;
int x[2010],y[2010];
signed main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            mp[make_pair(x[i]+x[j],y[i]+y[j])]++;
	for(map<pair<int,int>,int>::iterator it=mp.begin();it!=mp.end();it++){
		int t=it->second;
		ans+=t*(t-1)/2;
	}
    cout<<ans<<endl;
    return 0;
}

CF682D Alyona and Strings

题意

题目描述

给定两个字符串 \(s\)\(t\),长度分别为 \(n\)\(m\)。要求找出 \(k\)不相交的非空子串 \(p_1,p_2,...,p_k\),满足:

  1. 这些子串在 \(s\)\(t\)按相同顺序出现
  2. 子串在 \(s\)\(t\) 中均不相交(不重叠)
  3. 最大化这些子串的长度之和

数据范围

  • 字符串长度:\(1 \leq n,m \leq 1000\)
  • 子串数量 \(k\)\(1 \leq k \leq 10\)
  • 字符集:小写字母

题解

观察到 \(n,m\) 只有 \(1000\),考虑 \(nm\) dp。

\(dp[i][j][k][0/1]\) 表示字符串 \(s\) 的前 \(i\) 位与字符串 \(t\) 的前 \(j\) 位,一共截断了 \(k\) 次,以及当前位选或不选 (\(0/1\)).

转移显然(见代码)。

注意,这么 dp 可能导致 MLE,可以把 int 改成 short 或者用滚动数组优化(我用了前者,比较懒)。

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,p,ans;
char a[2010],b[2010];
short dp[2010][2010][11][2];
int main()
{
	cin>>n>>m>>p>>(a+1)>>(b+1);
	for(int i=0;i<=n;i++)
		for(int j=0;j<=m;j++)
			for(int k=1;k<=p;k++)
                dp[i][j][k][0]=dp[i][j][k][1]=-1e4;
	for(int i=1;i<=n;i++)
	{
        // cout<<i<<endl;
		for(int j=1;j<=m;j++)
		{
            // cout<<"=>"<<j<<endl;
			for(int k=0;k<=p;k++)
			{
				dp[i][j][k][0]=max(dp[i-1][j-1][k][0],max(dp[i-1][j][k][0],dp[i][j-1][k][0]));
				dp[i][j][k][0]=max(dp[i][j][k][0],max(dp[i-1][j-1][k][1],max(dp[i-1][j][k][1],dp[i][j-1][k][1])));
				if(a[i]==b[j] && k) dp[i][j][k][1]=max((int)dp[i][j][k][1],max(dp[i-1][j-1][k-1][0],dp[i-1][j-1][k][1])+1);
                // cout<<dp[i][j][k][0]<<" "<<dp[i][j][k][1]<<endl;
			}
            // cout<<endl;
		}
        // cout<<endl;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=0;k<=p;k++)
            {
                if(k==p) ans=max(ans,(int)max(dp[i][j][k][0],dp[i][j][k][1]));
                else
                {
                    if(dp[i][j][k][0]>=p) ans=max(ans,(int)dp[i][j][k][0]);
                    if(dp[i][j][k][1]>=p) ans=max(ans,(int)dp[i][j][k][1]);
                }
            }
	cout<<ans;
	return 0;
}

CF685B Kay and Snowflake

题意

题目大意

给定一棵以 \(1\) 为根、包含 \(n\) 个节点的树,进行 \(q\) 次查询。每次查询给出一个节点 \(v_i\),要求找出以 \(v_i\) 为根的子树的重心。重心的定义是:删除该节点后,剩余所有连通块的大小均不超过原子树大小的一半。

数据范围

  • 节点数 \(n\) 和查询次数 \(q\)\(2 \leq n, q \leq 3 \times 10^5\)
  • 树的输入方式:第 \(i\) 个节点(\(2 \leq i \leq n\))的父节点 \(p_i\) 给出,保证构成合法树结构
  • 保证每个查询的子树至少存在一个重心

题解

观察到,如果 \(u\)\(v\) 的父亲,那么 \(u\) 子树的重心的深度一定不大于 \(v\) 子树的重心的深度。

据此贪心即可,对于每个节点,取所有儿子中的最优解,因为每个点只会被访问一次,所以时间复杂度 \(O(n)\).

代码

#include<bits/stdc++.h>
using namespace std;
const int Maxn=3e5+10;
int n,q;
int mx[Maxn],ans[Maxn];
vector<int>edge[Maxn];
int sz[Maxn],fa[Maxn];
void dfs(int u)
{
	sz[u]=1;
	for(int i=0;i<edge[u].size();i++)
	{
		int v=edge[u][i];
		if(v==fa[u]) continue;
		dfs(v);
		sz[u]+=sz[v];
		mx[u]=max(mx[u],sz[v]);
	}
    ans[u]=u;
	for(int i=0;i<edge[u].size();i++)
	{
		int v=edge[u][i];
		if(v==fa[u]) continue;
		v=ans[v];
		while(fa[v]!=u && max(mx[v],sz[u]-sz[v])>=max(mx[fa[v]],sz[u]-sz[fa[v]])) v=fa[v];
		if(max(mx[v],sz[u]-sz[v])<max(mx[ans[u]],sz[u]-sz[ans[u]])) ans[u]=v;
	}
}
int main()
{
	cin>>n>>q;
	for(int i=2;i<=n;i++)
	{
		int u;
		cin>>u; fa[i]=u;
		edge[u].push_back(i);
		edge[i].push_back(u);
	}
	dfs(1);
    while(q--)
    {
        int u;
        cin>>u;
        cout<<ans[u]<<endl;
    }
}

CF687C The Values You Can Make

题意

给定 \(n\) 个硬币,面值分别为 \(c_1, c_2, \ldots, c_n\),以及一个目标金额 \(k\)。请找出所有满足以下条件的 \(x\)

  • 存在一个硬币子集,其和恰好为 \(k\)
  • 在该子集中,存在另一个子集,其和恰好为 \(x\)

请输出所有可能的 \(x\) 值(升序排列),包括 \(0\)\(k\)

\(1\leq c_i,n,k \leq 500\).

题解

考虑背包dp,\(dp_{i,j,k}\) 表示前 \(i\) 个物品,总价值为 \(j\),能否凑出面值为 \(k\)

很明显,\(dp_{i,j}\) 可以从 \(dp_{i,j-a_i}\) 转移,也可以从 \(dp_{i-1,j}\) 转移。

考虑对于新选择的 \(a_i\),如果 \(dp_{i,j-a[i],k-a[i]}\) 为真,那么 \(dp_{i,j,k}\) 为真。

代码

#include<bits/stdc++.h>
// #define int long long
using namespace std;
int n,k,cnt;
int a[510],ans[510];
bool dp[510][510][510];
signed main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=0;i<=n;i++) dp[i][0][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=k;j++)
        {
            for(int l=0;l<=j;l++)
            {
                if(j>=a[i] && l>=a[i]) dp[i][j][l]|=dp[i-1][j-a[i]][l-a[i]];
                if(j>=a[i]) dp[i][j][l]|=dp[i-1][j-a[i]][l];
                dp[i][j][l]|=dp[i-1][j][l];
            }
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=0;j<=500;j++)
            ans[j]|=dp[i][k][j];
    for(int i=0;i<=500;i++) if(ans[i]) cnt++;
    cout<<cnt<<endl;
    for(int i=0;i<=500;i++) if(ans[i]) cout<<i<<" ";
    cout<<endl;
    return 0;
}

CF691E Xor-sequences

题意

给定 \(n\) 个整数 \(a_1, a_2, ..., a_n\),定义长度为 \(k\) 的序列 \(x_1, x_2, ..., x_k\) 为“xor-sequence”,当且仅当满足:

  • 对于每一对相邻元素 \(x_i, x_{i+1}\)\(x_i \oplus x_{i+1}\) 的二进制表示中 1 的个数是 3 的倍数;
  • 序列中的每个 \(x_i\) 都是从给定的 \(a\) 数组中选取的(可以重复选取,但视为不同元素)。

问长度为 \(k\) 的“xor-sequence”有多少种,答案对 \(10^9+7\) 取模。

\(1\leq n \leq 100\)\(1\leq k\leq 10^{18}\)

题解

考虑建图,若 \(u,v\) 亦或起来满足第一个条件,那么将其连边,此时问题变为了:在途中有多少条长度为 \(k\) 的路径。

模板

仿照 floyd,设 \(f_t[i][j]\) 表示以 \(i\) 为起点,\(j\) 为终点的长度为 \(t\) 的路径数量。

转移显然:

\[f_t[i][j]=\sum_n f_{t-1}[i][k]\times f_1[k][j] \]

发现是矩阵乘法的形式,故用矩阵快速幂解决。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int n,k,ans;
int a[110];
struct MAT
{
    int n,m;
    int a[110][110];
    MAT():n(0),m(0) {memset(a,0,sizeof(a));}
}f;
MAT operator*(MAT x,MAT y)
{
    MAT re;
    re.n=x.n; re.m=y.m;
    for(int i=1;i<=re.n;i++)
    {
        for(int j=1;j<=re.m;j++)
        {
            for(int k=1;k<=x.m;k++)
            {
                re.a[i][j]+=x.a[i][k]*y.a[k][j]%mod;
                if(re.a[i][j]>=mod) re.a[i][j]-=mod;
            }
        }
    }
    return re;
}
void output(MAT x)
{
    for(int i=1;i<=x.n;i++)
    {
        for(int j=1;j<=x.m;j++) cout<<x.a[i][j]<<" ";
        cout<<endl;
    }
    cout<<endl;
}
MAT ksm(MAT x,int b)
{
    if(b==0) return x;
    if(b==1) return x;
    MAT re=ksm(x,b>>1);
    re=re*re;
    // output(re);
    if(b&1) re=re*x;
    return re;
}
bool check(int x)
{
    int cnt=0;
    while(x)
    {
        cnt++;
        x-=(x&-x);
    }
    return cnt%3==0;
}
signed main()
{
    cin>>n>>k; f.n=f.m=n;
    if(k==1)
    {
        cout<<n<<endl;
        return 0;
    }
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(check(a[i]^a[j])) f.a[i][j]=1;
    // output(f);
    f=ksm(f,k-1);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            ans+=f.a[i][j];
            if(ans>=mod) ans-=mod;
        }
    }
    cout<<ans<<endl;
    return 0;
}
posted @ 2025-05-22 15:04  crazy--boy  阅读(41)  评论(0)    收藏  举报