CF刷题记录 更新至 2024-04-22 (可用于蓝桥杯备赛)

文章目录

博客折叠块模板

<details>
    <summary>思路(点击展开)</summary>
     
</details>
 
<details>
    <summary>代码(点击展开)</summary>
 	
</details>

位运算

Codeforces Round 900 (Div. 3) E. Iva & Pav

思路:

1 0 9 10^9 109 转换为 2 32 2^{32} 232上的位,进行位运算, a [ x ] [ i ] a[x][i] a[x][i] 为到x为止第i位的1个数前缀和
对于与运算,如果当前i的前缀和不为 r − l + 1 r-l+1 rl+1 ,则这一位的与运算结果为 0 0 0
当找到从左往右第一个位置 i i i 1 1 1 使得 k k k在这位为 0 0 0,则与运算前缀大于 k k k
二分查找最后一个与运算前缀大于等于 k k k r r r

#define int long long
#define ld long double

using namespace std;

const int N = 2e5+10,md=998244353;

int t;

int a[N][40];
int l, k;

bool cheek(int l, int r, int k)
{
    for (int i = 32; i >= 0; i--)
    {-
        if ((a[r][i] - a[l-1][i]) == r - l + 1 && ((k >> i) & 1) == 0)
            return true;
        else if ((a[r][i] - a[l-1][i]) != r - l + 1 && ((k >> i) & 1) == 1)
            return false;
    }
    return true;
}


signed main()
{
    cin >> t;

    while (t--)
    {
        int n;
        cin >> n;
        for (int i = 1; i <= n; i++)
        {
            int tmp;
            cin >> tmp;

            for (int j = 0; tmp; j++)
            {
                a[i][j] = tmp % 2;
                tmp >>= 1;
            }
        }

        for (int i = 1; i <= n; i++)
        {
            for (int j = 0; j <= 32; j++)
                a[i][j] += a[i - 1][j];
        }

        int q;
        cin >> q;
        while (q--)
        {
            cin >> l >> k;

            int ll = l - 1, rr = n;
            while (ll < rr)
            {
                int mid = (ll + rr + 1) >> 1;
                if (cheek(l, mid, k))ll = mid;
                else rr = mid - 1;
            }

            if (ll < l)cout << -1 << " ";
            else cout << ll << " ";
        }
        cout << endl;
        for (int i = 1; i <= n; i++)
            for (int j = 0; j <= 32; j++)a[i][j] = 0;
    }

    return 0;
}
Codeforces Round 901 (Div. 2) C. Jellyfish and Green Apple

思路:

浮点数转二进制, a / b a/b a/b 的结果为 g c d ( a , b ) gcd(a,b) gcdab*最简分式 ( n / m ) (n/m) n/m的结果
苹果能分的前提是人数得是一个2的次幂数,通过切割只能分为形同 0.001 0.001 0.001的二进制小数
a / b a/b a/b 的二进制如果在从左到右的 s p sp sp 位为 1 1 1 ,则需要切割到这个情况
一个苹果切割到这一位共用 2 s p − 1 − 1 2^{sp-1}-1 2sp11 刀 , 得到 2 s p 2^{sp} 2sp 份这样的苹果
则要加上 ( m / ( 1 < < s p ) ) ∗ ( ( 1 < < s p ) − 1 ) (m /(1 << sp)) * ((1 << sp) - 1) (m/(1<<sp))((1<<sp)1)
结果乘上先前优化的gcd

#define int long long
#define ld long double

using namespace std;

int gcd(int a, int b)
{
	if (b)return gcd(b, a % b);
	else return a;
}

void op()
{
	int n, m;
	cin >> n >> m;

	if (n % m == 0)cout << 0 << endl;
	else
	{
		int tmp = m;
		int k = gcd(n, m);
		n /= k;
		m /= k;

		if (m % 2)
		{
			cout << -1 << endl;;
			return;
			
		}
		int fg = 0;

		int kk = m;
		while (kk)
		{
			if (kk & 1)fg++;
			kk >>= 1;
			if (fg > 1)
			{
				cout << -1 << endl;
				return;
			}
		}

		bool st = true;
		int sp = 1,ans=0;

		while (st&&sp<=33)
		{
			if (((n << sp)/m) & 1)
			{
				ans += (m / ((int)1 << sp)) * (((int)1 << sp) - 1);
			}
			if ((n << sp) % m == 0)
			{
				st = false;
			}
			sp++;
		}

		cout << ans * k << endl;
	}
}

signed main()
{
	int t;
	cin >> t;
	while (t--)op();

	return 0;

}

DP

Codeforces Round 901 (Div. 2) D. Jellyfish and Mex

思路:

对于大于 m e x mex mex 的数不做处理,把 0 0 0 删完为结束

d p [ j ] dp[j] dp[j] m e x mex mex 更新到 j j j 所需要的最小花费

用mex=i时更新到j,转移方程为 d p [ j ] = m i n ( d p [ j ] , d p [ i ] + i ∗ ( c n t [ j ] − 1 ) + j ) dp[j] = min(dp[j], dp[i] + i * (cnt[j] - 1) + j) dp[j]=min(dp[j],dp[i]+i(cnt[j]1)+j);

#define int long long
#define ld long double

using namespace std;

int dp[5010];
map<int, int>cnt;
int tmp;

void op()
{
	int n;
	cin >> n;
	cnt.clear();

	memset(dp, 0x3f, sizeof dp);

	for (int i = 1; i <= n; i++)
	{
		cin >> tmp;
		cnt[tmp]++;
	}

	int mex = 0;
	while (cnt[mex])mex++;

	dp[mex] = 0;

	for (int i = mex; i >= 1; i--)
	{
		for (int j = 0; j < i; j++)
		{
			dp[j] = min(dp[j], dp[i] + i * (cnt[j] - 1) + j);
		}
	}

	cout << dp[0] << endl;
}

signed main()
{

	int t;
	cin >> t;
	while (t--)op();

	return 0;

}
CodeTON Round 5 ( Div1+Div2 ) C. Tenzing and Balls

思路:

f [ i ] f[i] f[i] 为从 1~i 能删去的最多数

f [ i ] = m a x ( f [ i − 1 ] , i − j + 1 + f [ j − 1 ] ) f[i] = max( f[i-1] , i - j + 1+ f[j-1] ) f[i]=max(f[i1],ij+1+f[j1]) ( a [ j ] = a [ i ] a[j]=a[i] a[j]=a[i] , 删去 i i i j j j , 再加上前 j − 1 j-1 j1 可删去的最大数 )

对与 j j j 可预处理, f [ j − 1 ] − j + 1 f[j-1] - j + 1 f[j1]j+1 可根据 a [ j ] a[j] a[j] 存一个最大情况

m a [ a [ j ] ] = m a x ( m a [ a [ j ] ] , f [ j − 1 ] − j + 1 ) ma[a[j]] = max( ma[a[j]] , f[j-1] - j +1 ) ma[a[j]]=max(ma[a[j]],f[j1]j+1)

f [ i ] = m a x ( f [ i − 1 ] , i + m a [ a [ i ] ] ) f[i] = max( f[i-1] , i + ma[a[i]] ) f[i]=max(f[i1],i+ma[a[i]])

#define int long long
#define ld long double

using namespace std;

const int INF = -0x3f3f3f3f;

int t;
int f[200010];
int a[200010];

void op()
{
	int n;
	cin >> n;
	vector<int>ma(n + 1,INF);

	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}

	f[0] = 0;
	for (int i = 1; i <= n; i++)
	{
		f[i] = max(f[i - 1], i + ma[a[i]]);
		ma[a[i]] = max(ma[a[i]], f[i - 1] - i + 1);
	}
	cout << f[n] << endl;
}

signed main()
{
	cin >> t;
	while (t--)
	{
		op();
	}

	return 0;
}
Codeforces Round 903 (Div. 3) E. Block Sequence

思路:

d p [ i ] dp[i] dp[i] 为当 i   n i~n i n 为完美的最少删除次数

d p [ n ] = 1 , d p [ n + 1 ] = 0 ; dp[n]=1,dp[n+1]=0; dp[n]=1,dp[n+1]=0;

从后至前对于 d p [ i ] dp[i] dp[i] 更新

若直接删除当前点,则为 d p [ i + 1 ] + 1 dp[i+1]+1 dp[i+1]+1

若不删除 则为 $ min(dp[i+a[i]+1] , dp[i])$

i + a [ i ] + 1 i+a[i]+1 i+a[i]+1 a [ i ] a[i] a[i] 不能覆盖的位置

#define int long long
#define ld long double

using namespace std;

int t;

int dp[200010];
int a[200010];

void op()
{
	int n;
	cin >> n;

	memset(dp, 0x3f, sizeof dp);

	for (int i = 1; i <= n; i++)cin >> a[i];

	dp[n] = 1;
	dp[n + 1] = 0;

	for (int i = n-1; i >=1; i--)
	{
		dp[i] = min(dp[i + 1] + 1, dp[i]);

		if (a[i] + i + 1 <= n+1)
		{
			dp[i] = min(dp[i + a[i] + 1], dp[i]);
		}
	}
	
	cout << dp[1] << endl;
}

signed main()
{
	cin >> t;
	while (t--)
	{
		op();
	}

	return 0;
}
新疆大学线下赛暨天梯赛选拔赛二周目 L2-4

对于 类似八皇后问题类型,并且 当我们放到(i,j)时(j,i)也会被放置的情况

若当前情况为 i :

  • 此情况相对于 i-1情况可在右下角放一次,即加上 dp[i-1]
  • 此情况相对于 i-2 情况可相当于 i-2 的矩阵额外多了一圈,可将第一枚放在顶部任意位置(除左上角,第二枚就一定出现在右下角,和 i-1 情况重复),所以加上 dp[i-2] * (i-1)

状态转移方程 d p i = d p i − 1 + d p i − 2 ∗ ( i − 1 ) dp_i = dp_{i-1} + dp_{i-2} * (i-1) dpi=dpi1+dpi2(i1)

auto solve = [&]()->void
{
    dp[1]=1;
    dp[2]=2;
    
    if(n<3)cout<<dp[n];
    else
    {
        for(int i=3;i<=n;i++)
        {
            dp[i]=(dp[i-1]+dp[i-2]*(i-1)%md)%md;
        }
        cout<<dp[n];
    }
};

类似题目:

Codeforces Round 940 (Div. 2) and CodeCraft-23 C. How Does the Rook Move?

上面题目的变式

推出 d p dp dp 的 过程类似

若当前情况为 i :

  • 此情况相对于 i-1情况可在右下角放一次,电脑放不了,即加上 dp[i-1]
  • 此情况相对于 i-2 情况可相当于 i-2 的矩阵额外多了一圈,可将第一枚放在顶部与左边任意位置(除左上角,第二枚就一定出现在右下角,和 i-1 情况重复),所以加上 dp[i-2] * (i-1) * 2

状态转移方程 d p i = d p i − 1 + d p i − 2 ∗ ( i − 1 ) ∗ 2 dp_i = dp_{i-1} + dp_{i-2} * (i-1) * 2 dpi=dpi1+dpi2(i1)2

void init()
{
    dp[0] = 1;
    dp[1] = 1;
    dp[2] = 3;

    for (int i = 3; i <= 300000; i++)
    {
        dp[i] = (dp[i - 1] + dp[i - 2] * (i - 1) * 2 % md) % md;
    }
}

auto solve = [&]()->void
        {
            int n, k;
            cin >> n >> k;

            for (int i = 1; i <= k; i++)
            {
                int x, y;
                cin >> x >> y;

                if (x == y)n--;
                else n -= 2;
            }

            cout << dp[n] << endl;
        };

规律

Codeforces Round 902 (Div. 2) C. Joyboard

思路:

在k=1,k=2,k=3 时有解

  • 当 k=1 时为全0
  • 当 k=2 时:
  1. 若 m>=n,则先是 0 然后为 1~n,最后一位可以为n的倍数也符合,即n+m/n-1
  2. 若m<n则为 1~m 即 m
  • 当 k=3 时:
  • 只能在n+1位是第3个不同情况(大于n),且不能为n的倍数,即 (m-n)-(m/n-1)
  • 只在m>n时k=3有解
#define int long long
#define ld long double

using namespace std;

int t;

void op()
{
	int n, m, k;
	cin >> n >> m >> k;

	if (k > 3)cout << 0 << endl;
	else if (k == 1)
	{
		cout << 1 << endl;
	}
	else if (k == 2)
	{
		int f = m / n;
		if (m >= n)
		{
			cout << n + f - 1 << endl;
		}
		else
		{
			cout << m << endl;
		}
	}
	else if (k == 3)
	{
		int f = m / n;
		if (m > n)
		{
			cout << m - (n - 1) - f << endl;
		}
		else cout << 0 << endl;
	}
}

signed main()
{
	cin >> t;
	while (t--)
	{
		op();
	}

	return 0;
}

双指针

Educational Codeforces Round 152 (Div. 2) D. Array Painting

思路:

双指针找连续正数段

  • 若段中出现2,则更新两头的0的情况,若为涂色则改为true
  • 若无2,则优先更新左侧0,若左0已经为true,则更新右侧0

数组开头结尾特判


#define int long long
#define ld long double

using namespace std;

int t;
bool st[200010];
int a[200010];

void op()
{
	int n;
	cin >> n;

	int cnt = n;
	for (int i = 1; i <= n; i++)cin >> a[i];

	int k = 0;

	for (int i = 1; i <= n; i++)
	{
		if (a[i] > 0)
		{
			k++;
			if (a[i] == 2)
			{
				if (i - 1 != 0 && !st[i - 1])
				{
					cnt--;
				}
				int fg = 0;
				for (int j = i + 1; j <= n; j++)
				{
					if (a[j] == 0)
					{
						
						st[j] = true;
						cnt -= (j - i + 1);
						
						fg = j;
						break;
					}
				}
				if (fg)i = fg;
				else
				{
					cnt -= (n - i + 1);
					i = n;
				}
			}
			else if (a[i] == 1)
			{
				if (i-1!=0&&!st[i - 1])cnt--;
				int fg = 0;
				bool fg2 = false;
				for (int j = i + 1; j <= n; j++)
				{
					if (!fg2 && a[j] == 2)fg2 = true;
					if (a[j] == 0)
					{
						cnt -= (j - i);
						if (a[j - 1] == 2 || (i - 1 != 0 && st[i - 1]) || i - 1 == 0 || fg2)
						{
							cnt--;
							st[j] = true;
						}
						fg = j;
						break;
					}
				}

				if (fg)i = fg;
				else
				{
					cnt -= (n - i + 1);
					i = n;
				}
			}
		}
	}

	cout << k + cnt << endl;
}

signed main()
{
	t = 1;
	while (t--)
	{
		op();
	}

	return 0;
}

数论

Codeforces Round 891 (Div. 3) F. Sum and Product

思路:

对于x,y:ai+aj=x —> aj=x-ai

因此 ai*(x-ai) = y ——> ai = (x 土 sqr( x^2 - 4y ) ) /2

对应的 ai 就是要的两个值

  • 若两个值不同 a n s = ( c n t [ a 1 ] ∗ c n t [ a 2 ] ) ans = ( cnt[a1] * cnt[a2] ) ans=(cnt[a1]cnt[a2])

  • 若相同 则 a n s = ( c n t [ a 1 ] ∗ ( c n t [ a 1 ] − 1 ) ) / 2 ans = ( cnt[a1] * (cnt[a1]-1) )/2 ans=(cnt[a1](cnt[a1]1))/2

#define int long long
#define ld long double

using namespace std;

int t;

void op()
{
	int n, q, f;
	cin >> n;

	map<int, int>tmp;

	for (int i = 1; i <= n; i++)
	{
		cin >> f;
		tmp[f]++;
	}

	cin >> q;
	for (int i = 0; i < q; i++)
	{
		int x, y;
		cin >> x >> y;

		int num = x * x - 4*y;

		if (num < 0)cout << 0 << " ";
		else
		{
			int sqr = sqrt(num);
			if (sqr * sqr != num)cout << 0 << " ";
			else
			{
				if (num == 0)cout << tmp[(x - sqr) / 2] * (tmp[(x - sqr) / 2] - 1) / 2 << " ";
				else
				{
					int n1 = x - sqr, n2 = x + sqr;
					cout << tmp[n1/2] * tmp[n2/2] << " ";
				}
			}
		}
	}
	cout << endl;
}

signed main()
{
	cin >> t;
	while (t--)
	{
		op();
	}

	return 0;
}
Educational Codeforces Round 139 (Rated for Div. 2) D. Lucky Chains

思路:

假设幸运为k , 则 gcd(x+k,y+k) ≠ 1 , k取最小整数(k>=0)

由此可设 因子为 d d d , (x+k) % d = 0 , (y+k)%d = 0

因此 (y-x)%d = 0; 我们只需遍历 y-x 的所有质因子

对于这些质因子,找到大于等于 x 的最小倍数

答案即为 ((x + d - 1) / d) * d 取最小

#define int long long
#define LL long long
#define ld long double
#define fr(i,l,r) for(int i=l;i<=r;i++)
#define rf(i,r,l) for(int i=r;i>=l;i--)
#define ppb pop_back()
#define pb push_back
#define pf push_front
#define so(a) sort(a.begin(),a.end())
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define ikun ios_base::sync_with_stdio(false), cin.tie(NULL), cout.tie(0)
//mp.reserve(1024);
//mp.max_load_factor(0.25);

using namespace std;

const int N = 1e7 + 10,INF=1e9;

int pr[N];
bool st[N];
int n, cnt, m;
int sum1[N], d[N];// d 用于存储 i 的最小质因子
vector<int>as;

void p(int x) {//线性筛
    for (int i = 2; i <= x; i++)
    {
        if (!st[i])pr[cnt++] = i, d[i] = i;
        for (int j = 0; pr[j] <= x / i; j++)
        {
            st[pr[j] * i] = true;
            d[pr[j] * i] = pr[j];
            if (i % pr[j] == 0)break;
        }
    }
}

void getpr(int v)
{
    while (v > 1)
    {
        if (as.empty() || as.back() != d[v])as.pb(d[v]);

        v /= d[v];
    }
}

void solve()
{
    int a, b;
    cin >> a >> b;

    int s = b - a;

    if (s == 1)
    {
        cout << -1 << endl;
        return;
    }
    as.clear();

    getpr(s);

    int ans = INF;
    for (int it : as)
    {
        ans = min(ans, ((a + it - 1) / it) * it);
    }
    cout << ans-a << endl;
}

signed main()
{
    ikun;

    p(N - 1);

    int t;
    t == 1;
    cin >> t;

    while (t--)
    {
        solve();
    }

    return 0;
}
Codeforces Round 910 (Div. 2) B. Milena and Admirer

思路:

要使数组非递减,则可以先进行倒序遍历,对于当前的 a i a_i ai , 要使 a i ≤ a i + 1 a_i\le a_{i+1} aiai+1

我们可以进行贪心,让 a i a_i ai 分完尽可能使每个 a i / k ≤ a i + 1 a_i / k \le a_{i+1} ai/kai+1

因此 k 为 a i / a i + 1 a_i / a_{i+1} ai/ai+1 上取整 , a i a_i ai 更新为 a i / k a_i / k ai/k

a n s + = k − 1 ans += k - 1 ans+=k1( 分为 k 份 , 即划分 k-1 次 )

#define int long long
#define LL long long
#define ld long double
#define fr(i,l,r) for(int i=l;i<=r;i++)
#define rf(i,r,l) for(int i=r;i>=l;i--)
#define ppb pop_back()
#define pb push_back
#define pf push_front
#define so(a) sort(a.begin(),a.end())
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define ikun ios_base::sync_with_stdio(false), cin.tie(NULL), cout.tie(0)
//mp.reserve(1024);
//mp.max_load_factor(0.25);

//auto check = [&]()->bool
//    {
//
//    };

using namespace std;

const int N = 2e5 + 10;

int n;
int a[N];
int k;

signed main()
{
    ikun;

    int t;
    t = 1;
    cin >> t;

    auto solve = [&]()->void
        {
            cin >> n;
            for (int i = 1; i <= n; i++)
            {
                cin >> a[i];
            }

            int ans = 0;
            for (int i = n - 1; i >= 1; i--)
            {
                k = (a[i] + a[i + 1] - 1) / a[i + 1];
                //cout << "k: " << k << endl;
                a[i] = a[i] / k;
                ans += k - 1;
            }
            cout << ans << endl;
        };

    while (t--)
    {
        solve();
    }

    return 0;
}
Codeforces Round 910 (Div. 2) D. Absolute Beauty

思路:

将每个 a i a_i ai b i b_i bi 转化为线段,大数在后,小数在前

即 L ( min) —— R (max)

对于 b i b_i bi b j b_j bj 的 交换 :

  1. ​ L1 —— R1 L2 —— R2

若 两线段原本不相交,则会使两线段相交,长度和 += 2 ∗ ( L 2 − R 1 ) 2*( L2 - R1 ) 2(L2R1)

  1. 若原本相交,则会使线段分开,负贡献或者无贡献

因此要尽可能选用两条相隔最远的不相交线段

我们可以记录 最小的右端点 R ,找到最大的左端点 L

答案为 原所有线段和 s u m + sum + sum+ 2 ∗ ( M a x L 2 − M I N R 1 ) 2*( MaxL2 - MINR1 ) 2(MaxL2MINR1)

#define int long long
#define LL long long
#define ld long double
#define fr(i,l,r) for(int i=l;i<=r;i++)
#define rf(i,r,l) for(int i=r;i>=l;i--)
#define ppb pop_back()
#define pb push_back
#define pf push_front
#define so(a) sort(a.begin(),a.end())
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define ikun ios_base::sync_with_stdio(false), cin.tie(NULL), cout.tie(0)
//mp.reserve(1024);
//mp.max_load_factor(0.25);

//auto check = [&]()->bool
//    {
//
//    };

using namespace std;

const int N = 2e5 + 10;

int a[N], b[N];


signed main()
{
    ikun;

    int t;
    t = 1;
    cin >> t;

    auto solve = [&]()->void
        {
            int n;
            cin >> n;

            for (int i = 1; i <= n; i++)
            {
                cin >> a[i];
            }
            for (int i = 1; i <= n; i++)
            {
                cin >> b[i];
            }

            vector<pair<int, int>>s(n + 5);

            int sum = 0;

            for (int i = 1; i <= n; i++)
            {
                s[i] = minmax(a[i], b[i]);
                sum += abs(a[i] - b[i]);
            }

            int mx = 0;
            int r = (int)1e9;

            sort(s.begin() + 1, s.begin() + 1 + n);

            for (int i=1;i<=n;i++)
            {
                mx = max(mx, s[i].first - r);
                r = min(r, s[i].second);
            }

            cout << sum + 2*mx << endl;
        };

    while (t--)
    {
        solve();
    }

    return 0;
}
Educational Codeforces Round 159 (Rated for Div. 2) C. Insert and Equalize

思路:

首先对 a a a 进行排序, 然后对所有差值取gcd ,获得可用的最大因子 g c gc gc

答案有两种情况:

  • 一种是 a n + 1 a_{n+1} an+1 在 $a_1   ~   a_n$ 范围内,这时要获得最大的位置

  • 一种情况是 $a_1   ~   a_n$ 范围内 对 g c gc gc 无空可插入,因此 a n + 1 a_{n+1} an+1 = a n + g c a_n + gc an+gc

对两种情况:

  • 第一种所有数用 g c gc gc 加到 a n a_n an
  • 第二种 所有数用 g c gc gc 加到 a n + 1 a_{n+1} an+1
#define int long long
#define LL long long
#define ld long double
#define fr(i,l,r) for(int i=l;i<=r;i++)
#define rf(i,r,l) for(int i=r;i>=l;i--)
#define ppb pop_back()
#define pb push_back
#define pf push_front
#define so(a) sort(a.begin(),a.end())
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define ikun ios_base::sync_with_stdio(false), cin.tie(NULL), cout.tie(0)
//mp.reserve(1024);
//mp.max_load_factor(0.25);

using namespace std;

const int N = 1e5 + 10;

vector<int>a;

int gcd(int a, int b)
{
    if (b)return gcd(b, a % b);
    return a;
}

void solve()
{
    a.clear();
    int n;
    cin >> n;
    int x;
    fr(i, 1, n)
    {
        cin >> x;
        a.pb(x);
    }

    so(a);
    if (a[0] == a[n - 1])
    {
        cout << 1 << endl;
        return;
    }

    int gc = a[n - 1] - a[0];
    fr(i, 1, n - 1)
    {
        if (a[i] != a[i - 1])
        {
            gc = gcd(gc, a[i] - a[i - 1]);
        }
    }

    int ans = 0;
    int p = a[n - 1] + gc;
    fr(i, 0, n - 1)
    {
        ans += ( p - a[i]) / gc;
    }

    p = a[n - 1];
    rf(i,n - 1,0)
    {
        if (p == a[i])
        {
            p -= gc;
        }
        else
            break;
    }

    int res = (a[n - 1] - p) / gc;
    fr(i,0,n - 1)
    {
        res += (a[n - 1] - a[i]) / gc;
    }

    cout << min(ans, res) << "\n";
}

signed main()
{
    ikun;
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}
Codeforces Round 919 (Div. 2) C.Partitioning the Array

思路:

定理:对于两个数,我们想要其对m取模后余数相等,即

$a=k1m+q,b=k2m+q $

a − b = ( k 1 − k 2 ) ∗ m a-b = (k1-k2)*m ab=(k1k2)m

因此 m m m 应为 ∣ a − b ∣ |a-b| ab 的因子,本题要求 m ! = 1 m!=1 m!=1

因此若要所有对 k k k 相邻数模 m m m 相同

m = g c d ( ∣ a 1 − a 1 + k ∣ , ∣ a 2 − a 2 + k ∣ , … … , ∣ a n − k + 1 − a n ∣ ) m= gcd(|a_1 - a_{1+k}|,|a_2 - a_{2+k}|,……,|a_{n-k+1} - a_n|) m=gcd(a1a1+k,a2a2+k,……,ank+1an)

m ! = 1 m!=1 m!=1 ,则有解

auto gcd = [&](auto self, int a, int b)->int
{
    if (b == 0)return a;
    return self(self, b, a % b);
};//返回最大公因子


auto solve = [&]()->void
    {
        int n;
        cin >> n;

        for (int i = 1; i <= n; i++)
        {
            cin >> a[i];
        }

        if (n == 1)
        {
            cout << 1 << endl;
            return;
        }

        int ans = 0;
        int k;
        bool fg = false;
        for (int i = 1; i*i <= n; i++)
        {
            if (n % i == 0)
            {
                int m = 0;

                k = i;
                for (int j = 1; j + k <=n; j++)
                {
                    m = gcd(gcd, m, abs(a[j + k] - a[j]));
                }

                if (m != 1)ans++;

                if (i * i != n)
                {
                    m = 0;

                    k = n/i;
                    for (int j = 1; j + k <= n; j++)
                    {
                        m = gcd(gcd, m, abs(a[j + k] - a[j]));
                    }

                    if (m != 1)ans++;
                }
            }
        }

        cout << ans << endl;
    };

图论

2023湖南省赛 F 宝石交易

思路:

让上下两串宝石串相等,且改变最优

首先,对于它提供的所有改变方法用邻接矩阵存,看成图的边

通过Floyd更新所有可变到的情况,即更新多源最短路

遍历确定s的可能第一更新节点,循环遍历t,并与s比较

相同则跳过,不同就获取上下宝石的最优变化(使相等)

代码中为INF则为无变换可能,退出这一情况

对最后的ans判断即可

#define int long long
#define ld long double

using namespace std;

const int N = 400, INF = 0x3f3f3f3f;

int g[N + 1][N + 1];


signed main()
{
	int n, m;
	scanf("%lld %lld", &n, &m);

	memset(g, 0x3f, sizeof g);

	for (int i = 1; i <= N; i++)g[i][i] = 0;

	vector<int>s(n + 1);
	vector<int>t(2 * n + 1);

	for (int i = 1; i <= n; i++)
	{
		scanf("%lld", &s[i]);
	}

	for (int i = 1; i <= n; i++)
	{
		scanf("%lld", &t[i]);

		t[n + i] = t[i];
	}

	while (m--)
	{
		int a, b, c;
		scanf("%lld %lld %lld", &a,&b, &c);

		g[a][b] = min(g[a][b], c);
	}

	for (int k = 1; k <= N; k++)
	{
		for (int i = 1; i <= N; i++)
		{
			for (int j = 1; j <= N; j++)
			{
				g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
			}
		}
	}

	int ans = INT_MAX;
	for (int i = 1; i <= n; i++)
	{
		int l = i, r = i + n - 1;
		int sum = 0;
		bool ok = 1;

		for (int j = l, k = 1; j <= r; j++, k++)
		{
			if (s[k] == t[j])
			{
				continue;
			}
			int u = s[k], v = t[j];
			int mn = min(g[u][v], g[v][u]);
			if (mn == INF)
			{
				ok = 0;
				break;
			}
			sum += mn;
			if (sum >= ans)break;
		}
		if (ok)ans = min(ans, sum);
	}

	if (ans >= INT_MAX)printf("-1");
	else printf("%lld", ans);

	return 0;
}
Codeforces Round 903 (Div. 3) F. Minimum Maximum Distance

思路:

对标记点更新fg,从0开始进行bfs,更新d1为所有点到0的距离

获得到0最远的标记点L,从L开始bfs,更新d2为所有点到L的距离

获得距离L最远的标记点R,从R开始bfs,更新d3为所有点到R的距离

遍历所有点,这个点与标记点的最大距离一定为与L或R的距离

d i s [ i ] = m a x ( d 1 [ i ] , d 2 [ i ] ) dis[i] = max ( d1[i] , d2[i] ) dis[i]=max(d1[i],d2[i])

获得所有点的最小 d i s dis dis 即可

#define int long long
#define ld long double

using namespace std;

int t;
queue<int>q;

void op()
{
	int n, k;
	cin >> n >> k;

	vector<bool>fg(n, 0);

	for (int i = 0; i < k; i++)
	{
		int x;
		cin >> x;
		fg[x - 1] = 1;
	}

	vector<vector<int>>head(n);

	for (int i = 0; i < n - 1; i++)
	{
		int u, v;
		cin >> u >> v;
		head[u-1].push_back(v-1);
		head[v-1].push_back(u-1);
	}
//bfs,d1为1到点的距离
	vector<int>d1(n, -1);

	q.push(0);

	d1[0] = 0;

	while (q.size())
	{
		int x = q.front();
		q.pop();
		for (auto y : head[x])
		{
			if (d1[y] != -1)continue;
			d1[y] = d1[x] + 1;
			q.push(y);
		}
	}
//
	int L = 0, maxn1 = -1;
	for (int i = 0; i < n; i++)
	{
		if (!fg[i])continue;
		if (d1[i] > maxn1)
		{
			L = i;
			maxn1 = d1[i];
		}
	}

//bfs,d2为点到L的距离
	vector<int>d2(n, -1);

	q.push(L);

	d2[L] = 0;

	while (q.size())
	{
		int x = q.front();
		q.pop();
		for (auto y : head[x])
		{
			if (d2[y] != -1)continue;
			d2[y] = d2[x] + 1;
			q.push(y);
		}
	}
//
	int R = 0, maxn2 = -1;
	for (int i = 0; i < n; i++)
	{
		if (!fg[i])continue;
		if (d2[i] > maxn2)
		{
			maxn2 = d2[i];
			R = i;
		}
	}

//bfs,d3为点到R的距离
	vector<int>d3(n, -1);

	q.push(R);

	d3[R] = 0;

	while (q.size())
	{
		int x = q.front();
		q.pop();
		for (auto y : head[x])
		{
			if (d3[y] != -1)continue;
			d3[y] = d3[x] + 1;
			q.push(y);
		}
	}
//
	vector<int>dis(n, -1);
	for (int i = 0; i < n; i++)
	{
		dis[i] = max(d2[i], d3[i]);
	}
	int minn = 0x3f3f3f3f;
	for(int i = 0; i < n; i++)
	{
		minn = min(minn, dis[i]);
	}
	cout << minn << endl;
}

signed main()
{
	cin >> t;
	while (t--)
	{
		op();
	}

	return 0;
}
Codeforces Round 875 (Div. 2) C. Copil Copac Draws Trees

思路:

在输入树的边的同时记录他们的输入顺序

从 1 开始跑 DFS ,遇到未连上的边时 , 有两种情况(用 q 表示当前点的顺序序号)

  • 边的顺序在这个点连上之前,那么 DFS 的 deep 应 +1 , 即这条边应在 deep+1 次时连上

  • 边的顺序在这个点连上之后,那么 DFS 的deep不变 , 即这条边在 deep 次连上

对于 deep 取 max 即可

#define int long long
#define ld long double

using namespace std;

const int N = 2e5 + 10;

int t,n;
vector<pair<int, int>>h[N];
bool st[N];
int ans;

void dfs(int u, int q, int deep)
{
    for (auto it : h[u])
    {
        int v = it.first, p = it.second;

        if (st[v])
        {
            continue;
        }

        st[v] = true;

        if (p < q)
        {
            ans = max(ans, deep + 1);
            dfs(v, p, deep + 1);
        }
        else
        {
            ans = max(ans, deep);
            dfs(v, p, deep);
        }
    }
}

void solve()
{
    cin >> n;

    st[1] = true;
    for (int i = 1; i <= n; i++)
    {
        h[i].clear();
        st[i] = false;
    }

    for (int i = 1; i < n; i++)
    {
        int a, b;
        cin >> a >> b;

        h[a].push_back({ b,i });
        h[b].push_back({ a,i });
    }

    ans = 0;
    dfs(1, 0, 1);
    cout << ans << endl;
}

signed main()
{
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

数据结构

Codeforces Round 905 (Div. 2) C. You Are So Beautiful

定义:

设数组 abcd

  • 子数组定义:从原数组砍去前面若干元素,后边若干元素,剩余的数组。如:bc、ab
  • 子序列定义:从原数组删除若干元素,剩余元素拼凑一起,组成的数组。如:ac、bd

思路:

作为结果的连续子数组,如果他为 [ a l a_l al,……, a r a_r ar ]

则在 l 之前不能出现和 a l a_l al相同的数,否则子序列会相同

同理,r 之后不能出现和 a r a_r ar相同的数

因此,每个为最后一次出现的元素都可做结尾,开头必须为这个开头元素的第一次出现

用num存储当前经过的元素为开头位置的个数

当遍历到 a i a_i ai a i a_i ai相同元素结尾时,前面的所有开头元素都可作为开头, a i a_i ai为结尾, a n s + = n u m ans+=num ans+=num

#define int long long
#define ld long double

using namespace std;

const int N = 1e5 + 10;

int t, n, k, m;
int x, y, z;
int a[N];

void solve()
{
    cin >> n;
    map<int, int>fr, ed;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        x = a[i];
        if (!fr[x])
        {
            fr[x] = i;
            ed[x] = i;
        }
        else
        {
            ed[x] = i;
        }
    }

    int num = 0, ans = 0;

    for (int i = 1; i <= n; i++)
    {
        x = a[i];
        if (fr[x] == i)
        {
            ++num;
        }
        if (ed[x] == i)
        {
            ans += num;
        }
    }

    cout << ans << endl;
}

signed main()
{
    //t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}
Codeforces Round 580 (Div3) D. Distinct Characters Queries

树状数组板子题

建立 26个字母 的对应前缀和树状数组

string s;
int q, len;
int tr[26][100010];

int lowbit(int x) { return x & (-x); }

void add(int ch, int x, int k)
{
    while (x <= len)
    {
        tr[ch][x] += k;
        x += lowbit(x);
    }
}

int sum(int ch, int x)
{
    int tot = 0;
    while (x > 0)
    {
        tot += tr[ch][x];
        x -= lowbit(x);
    }
    return tot;
}

auto solve = [&]()->void
    {
        cin >> s;
        len = s.size();

        for (int i = 0; i < len; i++)
        {
            add(s[i] - 'a', i + 1, 1);
        }

        cin >> q;
        while (q--)
        {
            int op, x, y, ans = 0;
            char c;

            cin >> op;

            if (op == 1)
            {
                cin >> x >> c;

                add(s[x-1] - 'a', x, -1);
                add(c - 'a', x, 1);
                s[x-1] = c;
            }
            else
            {
                cin >> x >> y;

                for (int i = 0; i < 26; i++)
                {
                    ans += (sum(i, y) - sum(i, x - 1) > 0 ? 1 : 0);
                }
                cout << ans << endl;
            }
        }
    };

二分

Codeforces Round 905 (Div. 2) D1. Dances (Easy version)

思路:

对于a,它的头默认为1,则a[0]=1

对于排完序的a与b数组

最优为从a的结尾删除,从b的开头删除

二分保留位数,删去n-mid位,即a从0开始,b从 k(k=n-mid)开始

a 0 a_0 a0 ~ a m i d − 1 a_{mid-1} amid1 b k b_k bk ~ b n − 1 b_{n-1} bn1 比较大小

若满足则右移mid,更新最大的满足保留位数

则最小删去数为 n - l

#define int long long
#define ld long double

using namespace std;

const int N = 1e5 + 10;

int t, n, m;
int a[N],b[N];

bool check(int x)
{
    int k = n - x;
    for (int i = 0; i < x; i++)
    {
        if (a[i] >= b[i + k])
        {
            return false;
        }
    }
    return true;
}

void solve()
{
    cin >> n >> m;
    for (int i = 1; i < n; i++)
    {
        cin >> a[i];
    }

    a[0] = 1;
    for (int i = 0; i < n; i++)cin >> b[i];

    sort(a, a + n);
    sort(b, b + n);

    
    int l = 0, r = n, mid=0;
    while (l<r)
    {
        mid = (l + r+1) >> 1;//mid为a保留mid位,即删去n-mid位
        if (check(mid))
        {
            l = mid;
        }
        else r = mid - 1;
    }
    cout << n - l << endl;
}

signed main()
{
    //t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}
[Codeforces Round 936 (Div. 2) C.Tree Cutting](Codeforces Round 936 (Div. 2))

思路:

对于可分成的连通块中节点个数 node ($1 \le node \le n/(k+1) $)

因此对答案进行二分

我们的 c h e c k check check 使用 b f s bfs bfs 到底部后回溯更新当前点对应连通块的节点值

对于 > = m i d >= mid >=mid 的情况,直接分出当前连通块,并记录符合的块数

vector<int>edge[N];
int cnt;
int num[N];

void dfs(int u, int f, int v)
{
    num[u] = 1;
    for (int it : edge[u])
    {
        if (it == f)continue;
        dfs(it, u, v);
        num[u] += num[it];
    }
    if (num[u] >= v)
    {
        cnt++;
        num[u] = 0;
    }
    return;
}

signed main()
{
    ikun;

    int t;
    t = 1;
    cin >> t;

    auto solve = [&]()->void
        {
            int n, k;
            cin >> n >> k;

            for (int i = 1; i < n; i++)
            {
                int a, b;
                cin >> a >> b;

                edge[a].push_back(b);
                edge[b].push_back(a);
            }

            int l = 1, r = n / (k + 1);
            while (l < r)
            {
                cnt = 0;
                for (int i = 1; i <= n; i++)num[i] = 0;

                int mid = l + r + 1 >> 1;
                dfs(1, 1, mid);
                if (cnt < k + 1)r = mid - 1;
                else l = mid;
            }
            cout << l << endl;
            for (int i = 1; i <= n; i++)edge[i].clear();
            return;
        };

    while (t--)
    {
        solve();
    }

    return 0;
}

前缀和

Codeforces Round 904 (Div. 2) C. Medium Design

思路:

因为出现的线段应该为不相同的线段,所以最小值应该为 1 1 1 m m m

因此我们可以通过差分储存线段范围内的加值,再用前缀和表示这个范围内的最大加值

sl为不包含 1 1 1的线段的差分,sr为不包含 m m m的线段差分

记录用于差分的端点在 s 1 s1 s1 s 2 s2 s2

遍历 s 1 s1 s1 s 2 s2 s2,res+= s l n o d sl_{nod} slnod( s r n o d sr_{nod} srnod)则为 不到1的线段差分前缀和 与 不到m的线段差分前缀和

对于所取节点差分前缀和取 m a x max max即可得到最大差值

#define int long long
#define ld long double
#define fr(i,l,r) for(int i=l;i<=r;i++)
#define rf(i,r,l) for(int i=r;i>=l;i--)
#define pb push_back
#define so(a) sort(a.begin(),a.end())
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl

using namespace std;

const int N = 1e5 + 10;

int t, n, m;
map<int, int>sl, sr;
set<int>s1;
set<int>s2;


void solve()
{
    sl.clear();
    sr.clear();
    s1.clear();
    s2.clear();
    cin >> n >> m;

    fr(i, 1, n)
    {
        int l, r;
        cin >> l >> r;

        if (l > 1)
        {
            sl[l]++, sl[r+1]--;
            s1.insert(l), s1.insert(r+1);
        }
        if (r < m)
        {
            sr[l]++, sr[r+1]--;
            s2.insert(l), s2.insert(r+1);
        }
    }

    int ans = 0, res = 0;
    for (auto it:s1)
    {
        res += sl[it];
        ans = max(ans, res);
    }

    res = 0;
    for (auto it : s2)
    {
        res += sr[it];
        ans = max(ans, res);
    }

    cout << ans << endl;
}

signed main()
{
    //t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

后缀和

Codeforces Round 907 (Div. 2) B. Deja Vu

思路:

预处理31位的 2 x 2^x 2x 存在 t m p i tmp_i tmpi

对于输入 a i a_i ai,通过查找最后一个二进制1位置,存在 x 0 i x0_i x0i

由题意可知,对于输入的 x x x,如果有 a i a_i ai可整除 x x x,则会使 a i a_i ai加上 2 x − 1 2^{x-1} 2x1

所以之后除非是 x − 1 x-1 x1,否则无效,因此把输入 x x x保存降序部分在vector

x x x从后往前,将 2 x i 2^{x_i} 2xi后缀和存在sum

对于 a i a_i ai,二分查找在 x x x的第一个小于等于 x 0 i x0_i x0i的位置

a i a_i ai加上后缀和

#define int long long
#define ld long double
#define pb push_back
#define pf push_front

using namespace std;

int t;
int tmp[32];
int a[100010];
int x0[100010];
vector<int>x;
deque<int>sum;

void ini()
{
    tmp[0] = 1;
    for (int i = 1; i <= 31; i++)
    {
        tmp[i] = tmp[i - 1] * 2LL;
    }
}

void ss(int f, int i)
{
    int cnt = 0;
    while (f)
    {
        if (f & 1)
        {
            x0[i] = cnt;
            return;
        }

        cnt++;
        f >>= 1;
    }
}

int check(int f)
{
    int l = 0, r = x.size() - 1;

    while (l < r)
    {
        int mid = (l + r) >> 1;

        if (x[mid] > f)
        {
            l = mid + 1;
        }
        else r = mid;
    }
    return sum[r];
}

void solve()
{
    x.clear();
    sum.clear();
    int n, q;
    cin >> n >> q;

    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];

        ss(a[i], i);
    }

    for (int i = 1; i <= q; i++)
    {
        int z;
        cin >> z;
        if (!x.size() || x[x.size() - 1]>z)
        {
            x.pb(z);
        }
    }

    for (int i = x.size()-1; i>=0; i--)
    {
        if (i == x.size() - 1)
        {
            sum.pf(tmp[x[i]-1]);
        }
        else
        {
            sum.pf(tmp[x[i] - 1] + sum.front());
        }
    }

    for (int i = 1; i <= n; i++)
    {
        if (x0[i]>=x[x.size()-1])
        {
            a[i] += check(x0[i]);
        }
        cout << a[i] << " ";
    }
    cout << endl;
}

signed main()
{
    ini();
    //t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

交互机

Codeforces Round 931 (Div. 2) C

思路:

对于询问(1,1)和(n,m)点:

一定可以得到 x1+ y1 = ask(1,1) + 2 和 x2 + y2 = n + m - ask(n,m)

  • sum1 = ask(1, 1) + 2
  • sum2 = n + m - ask(n, m)

这时询问 (n,1)点,假设是(x1,y1)点符合

那么 x1 = (n + sum1 - 1 - ask(n,1) ) / 2 , y1 =sum1 - x1

然后 若(x1,y1)点存在且 ask(x1,y1) = 0,则成立 输出

否则 ask(n,1) 应为 ( x2 , y2 ) 同理代入 sum2 求得 ( x2 , y2 )

int a[] = {1,3,6,10};

void flush()//交互机刷新
{
    fflush(stdout);
}

int ask(int x, int y)
{
    cout << "? " << x << " " << y << endl;

    int res;
    cin >> res;

    return res;
}

signed main()
{
    ikun;

    int t;
    t = 1;
    cin >> t;

    auto solve = [&]()->void
        {
            flush();

            int n, m;
            cin >> n >> m;

            int sum1 = ask(1, 1) + 2;
            int sum2 = n + m - ask(n, m);
            int d1 = ask(n, 1);

            int x1 = (n + sum1 - 1 - d1) / 2;
            int y1 = sum1 - x1;

            if (x1 > 0 && x1 <= n && y1 > 0 && y1 <= m && ask(x1, y1) == 0)
            {
                cout << "! " << x1 << " " << y1 << endl;
            }
            else
            {
                int x2 = (n + sum2 - 1 - d1) / 2;
                int y2 = sum2 - x2;

                cout << "! " << x2 << " " << y2 << endl;
            }

        };
    while (t--)
    {
        solve();
    }

    return 0;
}
  • 37
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值