Codeforces Round 991 (Div. 3) A ~ G

zerocloud01發表於2024-12-10

Codeforces Round 991 (Div. 3) \(A \sim G\)

目錄
  • Codeforces Round 991 (Div. 3) \(A \sim G\)
    • A
      • 思路
      • 程式碼
    • B
      • 思路
      • 程式碼
    • C
      • 思路
      • 程式碼
    • D
      • 思路
      • 程式碼
    • E
      • 思路
      • 程式碼
    • F
      • 思路
      • 程式碼
    • G
      • 思路
      • 程式碼

Codeforces Round 991 (Div. 3)
補題, 不過 \(AK\)
廣告:starrycoding \(9\) 折優惠碼: FV7B04LL

A

思路

列舉字串到剛好下一個字串無法塞入.
注意: 每次要把當前點測試用例讀完.

程式碼

void func(void)
{
	int n,k;
	cin >> n >> k;
	string st;
	int X = 0, ans = 0;
	while(n --)
	{
		cin >> st;
		if(X + st.size() <= k)
		{
			X += st.size();
			ans ++;
		}
		else break;
	}
	while(n -- && n >= 0)	cin >> st;
	cout << ans << '\n';
}

B

思路

分析題意可得:
對於 \(i\), \(a_i-1\)\(a_{i+1}\) 可以交換值;
對於 \(i+1\), \(a_i\)\(a_{i+2}\) 可以交換值;
對於 \(i+2\), \(a_{i+1}\)\(a_{i+3}\) 可以交換值;

所以: \(a_i,a_{i+2},a_{i+4}.\ldots\) 可以交換數值.

所以: 奇數位之間可以傳遞值, 偶數位可以傳遞值.

只需要判斷奇數(偶數)的值總和是否可以均分到每個數上,
且分配後奇偶位置數值相同, 則可行.

程式碼

#define ll long long

void func(void)
{
	int n;
	cin >> n;
	vector<ll> a(n+1);
	ll sum1 = 0,sum2 = 0,sum = 0;
	for(int i=1;i<=n;++i)
	{
		cin >> a[i];
		sum += a[i];
		if(i&1)	sum1 += a[i];
		else	sum2 += a[i];
	}
	int cnt1 = (n+1)/2, cnt2 = n/2;
	if(sum % n == 0 && sum/n > 0 && 
	sum1%cnt1 == 0 && sum1 / cnt1 > 0 && 
	sum2%cnt2 == 0 && sum2 / cnt2 > 0 && 
	sum1/cnt1 == sum2/cnt2)
	{
		cout << "YES\n";
	}
	else	cout << "NO\n";	
}

C

思路

\(9\) 的倍數的 數位之和 也為 \(9\) 的倍數.

分析可得, 只有\(2,3\) 可以進行一次平方, 且有意義.
\(\mod 9\) 意義下,
對於2:

  • 一次平方使得總和 \(+(4-2 = 2)\).
  • 多次相加, 結果為: \(\{2,4,6,8,1,3,5,7\}\), \(8\) 為週期.
    對於3:
  • 一次平方使得總和 \(+(9-3 = 6)\).
  • 多次相加, 結果為: \(\{6,3,0\}\), \(3\) 為週期.
  • \(+0\) 無意義, 只需要考慮 \(2\) 次.

判斷在 \(\min(cnt_2,8)\)\(+2\), 和 \(\min(cnt_3,2)\)\(+6\) 任意組合下(最多 \(16種\)), 是否可以使得成為 \(9\) 的倍數.

程式碼

#define ll long long
void func(void)
{
	string st;
	cin >> st;
	ll sum = 0;
	int cnt2 = 0,cnt3 = 0;
	for(auto &i : st)
	{
		sum += (i-'0');
		if(i == '2')	cnt2 ++;
		if(i == '3')	cnt3 ++;
	}
	int stp1 = min(cnt2,8), stp2 = min(cnt3,2);
	for(int i=0;i<=stp1;++i)
	{
		for(int j=0;j<=stp2;++j)
		{
			if((i*2+j*6+sum) % 9ll == 0)
			{
				cout << "YES\n";
				return ;
			}
		}
	}
	cout << "NO\n";
}

D

最開始沒考慮最多移動 \(10\) 次, 還準備上線段樹維護.

思路

對於第 \(i\) 個數, 最多向前移動 \(a_i \le 9\)
或者說, 第 \(i\) 個位置最多使用 \(i \sim i+9\) 十個位置.
那麼貪心這 \(10\) 個位置種的最大值(減去代價後)即可.
注意: 若是有相同大小的數, 使用哪個不影響結果, 因為他們位移到兩個位置的總代價相同.

程式碼

void func(void)
{
	string st,ans;
	cin >> st;
	int len = st.size();
	for(int i=0;i<len;++i)
	{
		int len2 = min(i+10,len);
		int p = i;
		for(int j=i;j<len2;++j)
		{
			if(st[j] - (j-i) > st[p]-(p-i)) p = j;
		}
		st[p] -= (p-i);
		for(int j=p;j>i;--j)	swap(st[j-1],st[j]);
	}
	cout << st << '\n';
}

E

思路

dp
\(dp\) 低手不太會解釋.

dp[i][op][k], op 表示是字串 \(a\)(0) 還是字串 \(b\)(1),
也就表示, 將 \(a[i]/b[i]\) 加入作為第 \(k\) 個字元後的最大值.

\(dp[i][0][k]\)個字元為例:
如果上一個字元也是 \(a\), 那麼只能從 \(dp[i-1][0][k-1]\) 轉移.
如果上一個字元是 \(b\), 需要從 \(dp[k-i][1][k-1]\) 轉移, 因為需要保證 \(i\) 前的 \(a\) 字元總數 \(+\) \(b\) 字元總數是 \(k-1\).

然後列舉 \(1\sim k\) 即可, 具體情況和注意事項看程式碼註釋.

程式碼

void func(void)
{
	string a,b,c;
	cin >> a >> b >> c;
	int len1 = a.size(), len2 = b.size(), len = c.size();
	a = '0' + a,b = '0' + b, c = '0' + c;// 保證下標從 1 開始
	vector<vector<vector<int>>> dp(len+1,vector<vector<int>>(2,vector<int>(len+1)));
    // 三位陣列, 和 int dp[len+1][2][len+1] 同理, 事實上第一維開 max(len1,len2)即可. 
	for(int k=1;k<=len;++k)
	{
		for(int i=max(1,k-len2);i<=min(k,len1);++i)
        // 不能無腦從 1 開始, 因為如果 b 的所有字元數目都不足 k, 
        // 那麼必須從 a 種補足, 下一個迴圈同理. 
		{
			dp[i][0][k] = max(dp[i-1][0][k-1],dp[k-i][1][k-1]) + (c[k] == a[i]);
		}
		for(int i=max(1,k-len1);i<=min(k,len2);++i)
		{
			dp[i][1][k] = max(dp[i-1][1][k-1],dp[k-i][0][k-1]) + (c[k] == b[i]);
		}
	}
	int mx = 0;
	for(int i=1;i<=len;++i) mx = max({mx,dp[i][0][len],dp[i][1][len]});
	cout << len-mx << '\n';
}

F

線段樹!!!
(其實st表就行).

思路

\(a \mod m = b \mod m\)
可得:
\(|a-b| \mod m = 0\).
那麼對於連續的序列
\(i,i+1,i+2\)
\(|a_i-a_{i+1}|\mod m_1 = 0\)
\(|a_{i+1}-a_{i+2}|\mod m_2 = 0\).

\(x\)\(m\) 的因數, 那麼:
\(|a-b| \mod m = 0 \rightarrow |a-b| \mod x = 0\)

那麼對上述連續序列, 求 \(m_0 = \gcd(m_1,m_2)\) , \(m_0\) 即為 \(a_i,a_{i+1},a_{i+2}\), 的最大 \(m\).

所以用可以維護區間 \(\gcd\) 的容器維護即可.

線段樹st表都可以

不會 st表, 只有線段樹的了.
用線段樹維護連修改操作都不需要.

程式碼

st表

// 小秦提供
#include<bits/stdc++.h>

#define ull unsigned long long 
#define ll long long
#define inf 0x3f3f3f3f
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl

using namespace std;
const double eps=1e-6;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));  

void solve()
{
	int n,q;
	cin>>n>>q;
	vector<int>a(n+1);
	for(int i=1;i<=n;i++) cin>>a[i];
	
	vector<vector<int>>f(n+1,vector<int>(20));
	for(int i=1;i<n;i++) f[i][0]=abs(a[i+1]-a[i]);
	
	for(int j=1;j<=20;j++)//列舉區間長度
	{
		for(int i=1;i+(1<<j)-1<=n;i++)//列舉起點
		{
			f[i][j]=__gcd(f[i][j-1],f[i+(1<<(j-1))][j-1]);
		}
	}
	vector<int>nx(n+1);
	nx[n]=n;
	for(int i=n;i>=1;i--)
	{
		if(a[i]==a[i-1]) nx[i-1]=nx[i];
		else nx[i-1]=i-1; 
	}
	//for(int i=1;i<=n;i++) cout<<nx[i]<<endl;
	auto query=[&](int l,int r)
	{
		r--;
		int k=log2(r-l+1);
	    return __gcd(f[l][k],f[r-(1<<k)+1][k]);
	};
	
	while(q--)
	{
		int l,r;
		cin>>l>>r;
		if(nx[l]>=r) cout<<0<<" ";
		else cout<<query(l,r)<<" ";
	}
	cout<<endl;
}
	
	
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t;cin>>t;
	while(t--)
	solve();
	
	return 0;
}

線段樹

#include<bits/stdc++.h>
#define Start cin.tie(0), cout.tie(0), ios::sync_with_stdio(false)
#define PII pair<int,int> 
#define x first
#define y second
#define ull unsigned long long
#define int long long
using namespace std;

const int M = 1000000007;
const int N = 2e5 + 10;

int n,q;
vector<int> a(N),t(N<<2);

int gcd(int a,int b)
{
	return (a == 0 ? b : gcd(b%a,a));
}

void build_tree(int be=1,int ed=n,int p=1)
{
	t[p] = 0;
	if(be == ed)
	{
		t[p] = a[be];
		return ;
	}
	int mid = (be + ed) >> 1;
	build_tree(be,mid,p<<1), build_tree(mid+1,ed,p<<1|1);
	t[p] = gcd(t[p<<1],t[p<<1|1]);
}

int query(int l,int r,int be=1,int ed=n,int p=1)
{
	if(l <= be && ed <= r)	return t[p];
	int mid = (be + ed) >> 1, res = 0;
	if(l <= mid)	res = gcd(res,query(l,r,be,mid,p<<1));
	if(mid+1 <= r)	res = gcd(res,query(l,r,mid+1,ed,p<<1|1));
	return res ;
}

void func(void);

signed main(void)
{
	Start;
	int _ = 1;
	cin >> _;
	while(_--)	func();
	return 0;
}

void func(void)
{
	cin >> n >> q;
	vector<int> b(n+1);
	for(int i=1;i<=n;++i)	cin >> b[i];
	n --;
	for(int i=1;i<=n;++i)	a[i] = abs(b[i] - b[i+1]);
	if(n != 0)	build_tree();
	while(q--)
	{
		int l,r;
		cin >> l >> r;
		if(l == r)	cout << 0 << ' ';
		else	cout << query(l,r-1) << ' ';
	}
	cout << '\n';
}

G

思路

樹上dp
真不會講了.

程式碼

#include<bits/stdc++.h>
#define Start cin.tie(0), cout.tie(0), ios::sync_with_stdio(false)
#define PII pair<int,int> 
#define x first
#define y second
#define ull unsigned long long
#define int long long
using namespace std;

const int M = 1000000007;
const int N = 2e5 + 10;

int n,ans;
vector<int> a[N], dp(N);
void dfs(int p,int lp)
{
	vector<int> X;
	dp[p] = (int)a[p].size() - 2;
	ans = max(ans,dp[p]+2);
	for(auto &i : a[p])
	{
		if(i == lp)	continue;
		dfs(i,p);
		X.push_back(dp[i]);
	}
	if(!X.size())	return ;
	sort(X.begin(),X.end(),greater<int>());
	dp[p] += X[0] > 0 ? X[0] : 0;
	ans = max(ans,dp[p]+2);
	if(X.size() >= 2)	ans = max(ans,dp[p]+X[1]+2);
}

void func(void);

signed main(void)
{
	Start;
	int _ = 1;
	cin >> _;
	while(_--)	func();
	return 0;
}

void func(void)
{
	ans = 0;
	cin >> n;
	for(int i=1;i<=n;++i)	a[i].clear();
	for(int i=1;i<n;++i)
	{
		int l,r;
		cin >> l >> r;
		a[l].push_back(r);
		a[r].push_back(l);
	}
	dfs(1,0);
	cout << ans << '\n';
}

相關文章