9月杂题选做

9月杂题选做

CodeForces - 1366D

题意

给定数组\(a\),对于\(\forall a_i\),找到其两个因子\(d_1 > 1,d_2 > 1\) ,st. \(gcd(d_1 + d_2 ,a_i) = 1\) 若不存在输出-1

\[1 \leq n \leq 5\times 10^5\\ 2 \leq a_i \leq 10^7 \]

分析

若\(a_i \in Prime\) 显然不存在

考虑\(a_i = \prod p_i^{e_i}\) ,令\(d_1 = p_1,d_2 = p_2...p_\alpha\) 有$d_1 \equiv 0 (mod \ p_1),d_1 \neq 0(mod \ p_i[i > 1]),d_2 \equiv 0 (mod \ p_i[i > 1]),d_2 \neq 0(mod \ p_1) $

于是有\(d_1 + d_2 \neq 0 (mod \ p_i[i > 0])\)

代码

int prime[maxn];
bool vis[maxn];
bool is[maxn];

inline int euler_sieve(int n){
    int cnt = 0;
    for(int i = 2;i <= n;i++){
        if(!vis[i]) prime[cnt++] = i;
        for(int j = 0;j < cnt;j++){
            if(i * prime[j] > n) break;
            vis[i * prime[j]] = 1;
            if(i % prime[j] == 0) break;
        }
    }
    return cnt;
}
 
int main(){
    int cnt = euler_sieve(maxn - 3);
    int n = rd();
    VI v(n);
    for(int i = 0;i < n;i++)
        v[i] = rd(),is[v[i]] = 1;
    vector<pair<int,pii>> ans(maxn - 3);
    for(int i = 0;i < cnt;i++){
        for(int j = prime[i];j < maxn - 3;j += prime[i]){
            if(is[j]) {
                if(ans[j].fi < 2) ans[j].fi++;
                if(ans[j].fi == 1) ans[j].se.fi = prime[i],ans[j].se.se = 1;
                else if(ans[j].fi == 2) ans[j].se.se *= prime[i];
            }
        }
    }
    
    for(int i = 0;i < n;i++)
        if(ans[v[i]].fi != 2) printf("-1 ");
        else printf("%d ",ans[v[i]].se.fi);
    puts("");
    for(int i = 0;i < n;i++)
        if(ans[v[i]].fi != 2) printf("-1 ");
        else printf("%d ",ans[v[i]].se.se);
}

CodeForces - 1369D

题意

给出树的生长过程。

如果这棵树没有儿子节点,就长出一个儿子节点,如果已经有了一个儿子节点,就再长出两个儿子节点。

询问\(n\)次生长后的节点个数

\[1 \leq T \leq 10^4\\ 1 \leq n \leq 10^6 \]

分析

画个图找规律即可

\[a_i = 2a_{i-2} +a_{i-1} + [i \equiv 0 (mod \ 3)] \]

代码

int main(){
    int T = rd();
    VI dp((int)2e6 + 5);
    dp[1] = 0;
    dp[2] = 0;
    for(int i = 3;i < (int)2e6 + 5;i++)
        dp[i] = ((ll)mul(dp[i - 2],2) + dp[i - 1] + (i % 3 == 0)) % MOD;
    while(T--){
        int n = rd();
        printf("%d\n",mul(dp[n],4));
    }
}

CodeForces - 1311D

题意

给定\(a \leq b \leq c\),每次操作可以对任意一个数+1或者-1,求最小操作次数st. \(a | b且b | c\)

\[1 \leq t \leq 100\\ 1 \leq a \leq b \leq c \leq 10^4 \]

分析

考虑到\(a,b,c\)的值域,枚举\(a\),由于\(a | b\),有\(b = ka\),\(k \leq \lfloor \frac{MAX}{a} \rfloor\)

确定\(a,b\)后显然\(c\)可以贪心地被\(O(1)\)确定

因此总复杂度就是调和级数的复杂度

代码

inline pii get(int tar,int x){
	int res1 = x % tar;
	int res2 = tar - x % tar;
	if(res1 <= res2 && x != x % tar) return make_pair(x - x % tar,res1);
	else return make_pair(x - x % tar + tar,res2);
}

int main(){
	int T = rd();
	while(T--){
		int a = rd();
		int b = rd();
		int c = rd();
		int ans = 1e9;
		int x,y,z;
		x = y = z = 0;
		for(int i = 1;i <= 10000;i++){
			//int tmp = 0;
			int A = i;
			for(int j = A;j <= 20000;j+=A){
			int B = j;
			int C = get(B,c).fi;
			//cout << A << ' ' << B << ' ' << C << '\n';
			int cost = abs(a - i) + abs(B - b) + get(B,c).se;
			//	cout << cost << '\n';
			//if(i == 6) cout << i << ' ' << A << ' ' << B << ' ' << C << ' ' << cost << '\n';
			if(cost < ans) {
				ans = cost;
				x = A;
				y = B;
				z = C;
			}
			}
		}
		printf("%d\n",ans);
		printf("%d %d %d\n",x,y,z);
	}
}

CodeForces - 1322B

题意

给定长度为\(n\)的数组\(a\),计算

\[(a_1 + a_2) \oplus (a_1 + a_3) \oplus ...\oplus (a_1 +a_n)\\ \oplus (a_2 +a_3) \oplus ... \oplus (a_2 +a_n)\\ ... \\ \oplus (a_{n-1} + a_n) \]

\[2 \leq n \leq 400000\\ 1 \leq a_i \leq 10^7 \]

分析

题目相当于\(n\)个元素两两异或求值

从二进制考虑,每一位的结果是否为1由这\(O(n^2)\)个数的1的个数决定。

考虑如何计算第\(i\)位1的个数,可以知道要这一位为1,两数的和要求是两段连续的区间,于是只需对\(a\)的前\(i\)位部分排序,就可以二分算出能够加出1的个数

根据总的\(1\)的个数确定答案第\(i\)位

代码

inline int cal(int x,VI &b){
    if(*b.rbegin() <= x) return b.size();
    return lower_bound(all(b),x + 1) - b.begin();
}

inline int cal(int l,int r,VI &b){
    return cal(r,b) - cal(l - 1,b);
}

int main(){
    int n = rd();
    VI a(n),b(n);
    for(int i = 0;i < n;i++)
        a[i] = rd();
    int ans = 0;
    for(int i = 0;i < 25;i++){
        for(int j = 0;j < n;j++)
            b[j] = a[j] % (1 << (i + 1));
        sort(all(b));
        ll c = 0;
        int dv = 1 << i;
        for(int j = 0;j < n;j++){
            c += cal(dv - b[j],2 * dv - 1 - b[j],b);
            c += cal(3 * dv - b[j],4 * dv - 1 - b[j],b);
        }
        for(int j = 0;j < n;j++)
            if((b[j] * 2) & dv) c--;
        c >>= 1;
        if(c & 1) ans += dv;
    }
    printf("%d",ans);
}

CodeForces - 1277D

题意

给定\(n\)个互不相同的01串,要求拼接成的串在拼接的位置首尾字符相同

现每次操作可以对串翻转,求最小翻转次数st. 存在一种合法的拼接方式

\[1 \leq n \leq 2e5 \]

分析

我们只关注01串的首尾字符,只有本质不同的4种:00,01,10,11

考虑00和11都可以一次性拼完,于是可以只考虑01,10,容易发现总是交替拼接,因此最终01和10都是一半的数量级,于是让多的那部分翻转即可,记得判以下翻转的情况

代码

int main(){
    int T = rd();
    while(T--){
        int n = rd();
        string s;
        int cnt[4] = {0};
        VI v(n);
        map<string,bool> mp;
        vector<string> vv(n);
        for(int i = 0;i < n;i++){
            cin >> s;
            vv[i] = s;
            mp[s] = 1;
            if(s.length() == 1) {
                if(s[0] == '1') cnt[3]++,v[i] = 3;
                else cnt[2]++,v[i] = 2;
            }
            else {
                if(s[0] == '0' && s[s.length() - 1] == '1') cnt[0]++,v[i] = 0;
                else if(s[0] == '1' && s[s.length() - 1] == '0') cnt[1]++,v[i] = 1;
                else if(s[0] == '1') cnt[3]++,v[i] = 3;
                else cnt[2]++,v[i] = 2;
            }
        }
        //cout << cnt[0] << ' ' << cnt[1] << ' ' << cnt[2] << ' ' << cnt[3] << '\n'; 
        if(!cnt[0] && !cnt[1]) {
            if(cnt[2] && cnt[3]) {
                puts("-1");
                continue;
            }
            else {
                puts("0");
                puts("");
                continue;
            }
        }
        int num = cnt[0] + cnt[1];
        int d = num / 2;
        VI ans;
        if(cnt[0] <= cnt[1]) {
            int del = abs(d - cnt[0]);
            for(int i = 0;i < n;i++){
                if(!del) break;
                string ss = vv[i];
                reverse(all(ss));
                if(v[i] == 1 && !mp[ss]) ans.push_back(i + 1),del--;
            }
        }
        else {
            int del = abs(d - cnt[1]);
            for(int i = 0;i < n;i++){
                if(!del) break;
                string ss = vv[i];
                reverse(all(ss));
                if(v[i] == 0 && !mp[ss]) ans.push_back(i + 1),del--;
            }
        }
        printf("%d\n",(int)ans.size());
        for(int i = 0;i < (int)ans.size();i++){
            printf("%d ",ans[i]);
        }
        puts("");
    }
}
上一篇:[做题记录-乱做] [AGC004F] Namori


下一篇:2021.10.1 QBXT