2021ccpc 网络赛 补题 Time-division Multiplexing(二分) && Power Sum && Function

Cut The Wire-hdu-7100

偶数贡献:
n n n 为奇数时,贡献为 ( n + 1 ) / 2 (n+1)/2 (n+1)/2 n n n 为偶数时,贡献为 n / 2 n/2 n/2,显然可以合成一个式子计算
奇数贡献:
总的奇数个数 − - 不满足条件的奇数个数
不满足条件的奇数 :
如果 n % 3 = = 0 n \% 3==0 n%3==0,最大的一个不满足条件的奇数是 < n / 3 − 1 < n / 3 - 1 <n/31 的。
否则就是 < n / 3 <n / 3 <n/3
+ 1 +1 +1 / 2 /2 /2 向上取整就得到这个范围内奇数的个数

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 9;
ll n, m, k, l, r, ans;
char s[maxn];
void work()
{
    scanf("%lld", &n);
    ans = 0;
    ans += (n + 1) / 2;
    ans += (n + 1) / 2 - ((n % 3 == 0 ? n / 3 - 1 : n / 3) + 1) / 2;
    printf("%lld\n", ans);
}

int main()
{
    int TT;cin>>TT;while(TT--)
    work();
    return 0;
}

Time-division Multiplexing
题意: n n n 个字符串(只包含小写字母),每个长度不超过 12 12 12,构造一个字符串 t t t,从 n n n 个字符串中第一个字符串开始,每次按顺序依次选取一个字符串的字符加入,如果一个字符串选到了最后一个,那么下次从这个字符串第一个开始选。求 t t t 串中最短的一个子串,满足包含 n n n 个字符串中所有种类的字符。
思路:所有字符串的长度的 l c m lcm lcm n n n 倍 就是 t t t 的循环周期
l c m lcm lcm 最大是 5 × 7 × 8 × 9 × 11 = 27720 5 \times 7 \times 8 \times 9 \times 11 = 27720 5×7×8×9×11=27720
问题就变成了对一个最大循环周期为 27720 n 27720n 27720n 的字符串求解。
需要注意的是,这个 t t t 串是无限循环串,最后的答案可能取值两个循环串之间,因此需要对两倍长度的循环节求解。
统计一下有多少中字符,模拟出 t t t 串的循环节并加倍,对循环节的2倍二分答案
参考题解

#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define _ 0
using namespace std;
const int maxn = 6e6 + 9;
const ll mod = (ll)1e9 + 7;
ll n, m;

char s[109][15];
char t[maxn];// 构造的 t 串 
int len[109];// 字符串长度 
int pos[109];// 字符串指针 
bool vis[30];
int num[30];// 统计某个字符数量,便于加减判断区间是否存在这个字符
ll kind, length;// 字符种类数 和 循环字符长度 

bool check(int mid)
{
	memset(num, 0, sizeof(num));
	int k = 0;
	for(int i = 0; i < mid; ++i)
	{
		if (num[t[i] - 'a'] == 0) k++;
        num[t[i] - 'a']++;
	}
	if(kind == k) return 1;
	int l = 0;
	for(int r = mid; r < 2 * length; ++l, ++r)
	{
		if(--num[t[l] - 'a'] == 0) --k;
		if(++num[t[r] - 'a'] == 1) ++k;
		if(k == kind) return 1;
	}
	return 0;
}

void work()
{
	memset(pos, 0, sizeof(pos)); memset(vis, 0, sizeof(vis));
    cin >> n;
    kind = 0;
    for(int i = 1; i <= n; ++i)
    {
    	cin >> s[i]; len[i] = strlen(s[i]);
    	for(int j = 0; j < len[i]; ++j) if(!vis[s[i][j] - 'a'])
    		++kind, vis[s[i][j] - 'a'] = 1;
	}
	
	bool f = 1;// 模拟出 t 串的循环节
	length = 0;
	while(f)
	{
		for(int j = 1; j <= n; ++j)
			t[length++] = s[j][pos[j]], pos[j] = (pos[j] + 1) % len[j];// 0 - len[j] - 1 
		f = 0;
		// pos[i] 全部等于0,意味着恰好一个周期,所有字符串指针指向第一个字符 
		for(int j = 1; j <= n; ++j) if(pos[j] != 0){
			f = 1; break;
		}
	}
	for(int j = 0; j < length; ++j)
		t[length + j] = t[j];
	t[length * 2] = 0;
	
	int l = kind, r = length;// 二分答案
	while(l < r)
	{
		int mid = (l + r) >> 1;
		if(check(mid)) r = mid;
		else l = mid + 1;
	}
	cout << l << endl;
}

int main()
{
	//ios::sync_with_stdio(0);
	int TT;cin>>TT;while(TT--)
    work();
	return ~~(0^_^0);
}

Power Sum-hdu-7105
题意:
给定一个数 n n n,求一个长度为 k k k 的数组 a a a a i ∈ a_i \in ai { − 1 , 1 -1,1 1,1}, k ∈ [ 1 , n + 2 ] k \in [1,n+2] k[1,n+2],满足下列等式
∑ i = 1 i = k a i × i 2 = n \sum_{i=1}^{i=k}a_i \times i^2=n i=1i=kai×i2=n
求一个长度为 k k k 01 01 01 串, 0 0 0 代表 a i = − 1 a_i=-1 ai=1 1 1 1 代表 a i = 1 a_i=1 ai=1,满足上述等式。
思路:
这个题要发现一个性质:
( x + 1 ) 2 (x+1)^2 (x+1)2 − - ( x + 2 ) 2 (x+2)^2 (x+2)2 − - ( x + 3 ) 2 (x+3)^2 (x+3)2 + + + ( x + 4 ) 2 (x+4)^2 (x+4)2 = = = 4 4 4
用四个数为构造 n n n 贡献 4 4 4 的大小,然后 n n n m o d mod mod 4 4 4 = 0 , 1 , 2 , 3 =0,1,2,3 =0,1,2,3,我们还可以发现 1 = 1 1=1 1=1 4 − 1 = 3 4-1=3 41=3 − 1 − 4 − 9 + 16 = 2 -1-4-9+16=2 149+16=2,这样最后缺少的 1 , 2 , 3 1,2,3 1,2,3 就都构造出来了。
AC代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 9;
ll n, x, k, m;

void work()
{
	cin >> n;
	m = n / 4;
	k = n % 4;
	string s = "";
	for(int i = 1; i <= m; ++i)
		s += "1001";
	if(k == 1) s = "1" + s;
	else if(k == 2) s = "0001" + s;
	else if(k == 3) s = "01" + s;
	cout << s.size() << endl;
	cout << s << endl;
}

int main()
{
	ios::sync_with_stdio(0);
	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

Function-hdu-7106
题意:给你一个函数 g ( x ) = x g(x)=x g(x)=x的各位和,和一个函数 f ( x ) f(x) f(x),求 x ∈ [ 1 , n ] x \in [1,n] x[1,n] f ( x ) m i n f(x)_{min} f(x)min
思路:
∀ x ∈ [ 1 , 1 0 6 ] \forall x \in [1,10^6] x[1,106] g ( x ) m a x = g ( 999999 ) = 54 g(x)_{max}=g(999999)=54 g(x)max=g(999999)=54,因此函数 g ( x ) ∈ [ 1 , 54 ] g(x) \in [1,54] g(x)[1,54],我们可以预处理出来所有 g ( x ) g(x) g(x) 对于取的 x x x 的值。枚举 g ( x ) g(x) g(x) 的取值,那么 f ( x ) f(x) f(x) 就是一个二次函数,三分寻找最小值即可。
f ( x ) = ( A g ( x ) + B ) x 2 + ( C g 2 ( x ) + D g ( x ) ) x f(x)=(Ag(x) + B)x^2+(Cg^2(x)+Dg(x))x f(x)=(Ag(x)+B)x2+(Cg2(x)+Dg(x))x

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 9;
ll n, m;
ll a, b, c, d;
vector <int> v[60];
inline ll f(ll A, ll B, ll x)
{
	return A * x * x + B * x;
}
void work()
{
	cin >> a >> b >> c >> d >> n;
	ll ans = 1e18;
	for(ll i = 1; i <= 54; ++i)// 枚举 g(x) 
	{
		if(v[i][1] > n) continue;// x可取范围不在定义域 [1,n] 内 
		ll l = 1, r = --upper_bound(v[i].begin(),v[i].end(), n) - v[i].begin();// r是最后一个满足小于等于n的数
		ll xa = a * i + b, xb = c * i * i + d * i;
		
		ans = min({ans, f(xa, xb, 1ll*v[i][l]), f(xa, xb, 1ll*v[i][r])});
		// 如果 xa <= 0 那么取值应该为 端点最小值 
		//  xa > 0,最小值在对称轴附近
		if(xa > 0)// 三分寻找最小值 
		{
			while(l <= r)
			{
				ll lmid = l + (r - l) / 3, rmid = r - (r - l) / 3;
				ll fl = f(xa, xb, 1ll*v[i][lmid]), fr = f(xa, xb, 1ll*v[i][rmid]);
				if(fl <= fr) r = rmid - 1;
				else l = lmid + 1;
				ans = min({ans, fl, fr});
			}
		}
	}
	cout << ans << endl;
}

int main()
{
	ios::sync_with_stdio(0);
	for(int i = 1; i <= 54; ++i) v[i].push_back(-1e9);
	for(int i = 1; i <= 1e6; ++i)
	{
		int sum = 0, tmp = i;
		while(tmp)
		{
			sum += tmp % 10;
			tmp /= 10;
		}
		v[sum].push_back(i);
	}
	for(int i = 1; i <= 54; ++i) v[i].push_back(1e9);
	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

Command Sequence-hdu-7108
题意:寻找子序列满足机器人在执行完这个序列之后仍在原点。
思路:计算贡献
AC代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 9;
ll n, m;
ll dp[maxn][2];
char s[maxn];
void work()
{
    scanf("%lld", &n);
    scanf("%s",s + 1);
    for(int i = 1; i <= n; ++i)
    {
        dp[i][0] = dp[i-1][0];
        dp[i][1] = dp[i-1][1];
        if(s[i] == 'U') ++dp[i][0];
        else if(s[i] == 'D') -- dp[i][0];
        else if(s[i] == 'L') ++dp[i][1];
        else --dp[i][1];
    }
    ll ans = 0;
    map<pair<int,int>,int> ma;
    ma[{0,0}] = 1;// 注意同时为0时的贡献
    for(int i = 1; i <= n; ++i)
    {
        pair <int,int> p = {dp[i][0], dp[i][1]};
        if(!ma.count(p)) ma[p] = 1;
        else ans += ma[p], ma[p]++;
    }
    printf("%lld\n", ans);
}

int main()
{
    int TT;cin>>TT;while(TT--)
    work();
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值