2022SCUT Winter Training Day 9

A. Ehab the Xorcist

Problem - 1325D - Codeforces

题意:给定u,v,构造一个尽可能短的数组,使得数组中所有数字异或和为u,和为v。

解法:

1.首先几个特判:

  • (1)u==v==0,答案是0
  • (2)u==v≠0,答案为长度1的数列{u}
  • (3)u>v或者u和v奇偶性不同,则没有答案,输出-1

2.对于其他情况,我们发现必定可以分成3项:{u,x,x},其中x = (v-u)/2,使得这个长度为3的数组满足题意。由于长度为1的显然不行,我们只需考虑能否把长度缩短到2。还是从刚才那个构造的例子出发,只要(u&x)==0,那么就可以把u,x并为一项u+x,异或和不会改变,这样就得到数组{u+x,x}

#include <bits/stdc++.h>
using namespace std;
#define debug cout<<"!!!"<<endl;
#define int long long
int u, v;

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin>>u>>v;
	if(u>v || (u+v)%2) {cout<<-1; return 0;}
    if(u==v && u==0) {cout<<0; return 0;}
    if(u==v) {cout<<1<<'\n'<<u; return 0;}
    int x = (v-u)/2;
    if((u&x)==0) {cout<<2<<'\n'<<u+x<<' '<<x; return 0;}
    else {cout<<3<<'\n'<<x<<' '<<x<<' '<<u; return 0;}
}

B. Yet Another Palindrome Problem 

Problem - B - Codeforces

题意:给定一个数组,寻找是否有长度为3的回文子序列。

解法:水题,只要找到距离>1的相同数字,说明就有回文子序列。否则没有。

#include <bits/stdc++.h>
using namespace std;
#define debug cout<<"!!!"<<endl;
const int N = 5005;
int t,n,x,p[N]; //p数组用来记录每个数字最先出现的位置

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin>>t;
    while(t--){
        memset(p,0,sizeof(p));
        cin>>n; bool flag = 0;
        for(int i=1; i<=n; i++){
            cin>>x;
            if(p[x]==0) p[x]=i;
            else if(p[x]<i-1) flag = 1;
        }
        puts(flag ? "YES" : "NO");
    }
}

C. EhAb AnD gCd 

Problem - A - Codeforces

题意:设x = gcd(a,b)+lcm(a,b),给定x,构造一组{a,b}

解法:简单构造题,令a=1,b=x-1即可

#include <bits/stdc++.h>
using namespace std;
#define debug cout<<"!!!"<<endl;
int t;

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin>>t;
    while(t--){
        int x; cin>>x;
        cout<<1<<' '<<x-1<<'\n';
    }
}

 D. Ehab's REAL Number Theory Problem

Problem - 1325E - Codeforces

思路:因数个数<=7,等效于质因数个数<=2(因为如果有3个,因数个数至少为2^3=8)。首先,把质因数的次数%2对答案没有影响。这样处理之后,可以分成1*1或1*p或p*q的形式。

  1. 如果是1*1形式,说明本身就是完全平方数,答案为1,直接输出。
  2. 另外还要注意重复出现的数,如果有重复数,则有一种答案为2的方案。(x*x肯定是完全平方数)
  3. 如果是1*p形式或者p*q形式,考虑处理成一个图,在1和p之间(或p和q之间)建一条边,然后找最小环,环大小就是答案。(为什么呢?因为环里面,每个点 i 都连着两条边,而每连一条边,就说明有一个这个质因数 i ,所以两条边就凑成 i ^2)

如果暴力求最小环的话,要首先枚举一个起点,然后bfs找环,这样复杂度是O(N^2),过不了,所以需要优化。实际上枚举起点只需要枚举到1e3即可,因为每条边连接两个因子,a[i] <= 1e6,其中较小的因子肯定 <= 1e3。所以每个环肯定至少有一个 <= 1e3的数,这样枚举起点能保证不会遗漏环。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5, inf=0x3f3f3f3f;
vector<int> g[N], num;
int dis[N], vis[N], pre[N];

inline void add(int a,int b){g[a].push_back(b);g[b].push_back(a);} //添加边
inline void divide(int x){ //分解质因数
    num.clear();
    int t = x;
    for(int i=2; i*i<=x; i++){
        if(t%i) continue;
        int cnt = 0;
        while(t%i==0){
            t/=i;
            cnt++;
        }
        if(cnt&1) num.push_back(i);
    }
    if(t>1) num.push_back(t);
}
inline int bfs(int s){ //bfs找最小环(二元环不算)
    memset(dis,inf,sizeof(dis)); //重置距离和前驱
    memset(pre,0,sizeof(pre));
    int res = inf; //以s为起点的最小环长度(inf表示没有环)
    queue<int> q; //bfs要用到队列
    q.push(s); dis[s] = 0; pre[s] = 0; //处理起点
    while(q.size()){
        int u = q.front(); q.pop();
        for(int v:g[u]){
            if(v==pre[u]) continue; //这步很重要,防止二元环出现
            if(dis[v]!=inf) {res = dis[u]+dis[v]+1; break;} //会和成功,结束循环
            dis[v] = dis[u]+1; pre[v] = u; //记录距离和前驱
            q.push(v); //加入子结点
        }
        if(res != inf) break; //找到环就结束
    }
    return res;
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int n, ans=inf;
    cin>>n;
    for(int i=1; i<=n; i++){
        int x; cin>>x;
        if(vis[x]) ans = min(ans, 2); //x出现过,两个x可组成完全平方
        vis[x] = 1;
        divide(x); //把x进行因数分解
        if(num.size()==2) add(num[0],num[1]);
        else if(num.size()==1) add(1,num[0]);
        else ans = 1;
    }
    if(ans<=2) {cout<<ans; return 0;} //剪枝,因为bfs不会产生<=2的解
    /* 关于这里为什么只需要枚举到1000:
        一个数a[i]分成两个因子p,q,那么min(p,q) <= sqrt(a[i]) <= 1e3
        所以,任意一条边都会连接至少一个 <= 1e3的数字,从而每一个环里必定至少有一个 <= 1e3的数字
    */
    for(int i=1; i<=1000; i++){
        ans = min(ans, bfs(i));
    }
    if(ans==inf) cout<<-1;
    else cout<<ans;
}

F. Frog Jumps 

Problem - 1324C - Codeforces

解法:贪心,跳到L块上是没用的。所以只用考虑R块,答案就是相邻R块的最长距离(代码里把起点和终点也看成R块,方便处理)

#include <bits/stdc++.h>
using namespace std;
#define debug cout<<"!!!"<<endl;
int t; string s;
signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin>>t;
    while(t--){
        cin>>s; s = 'R'+s+'R';
        int d=1, maxd=1;
        for(char ch:s){
            maxd = max(maxd,d);
            if(ch=='R') d=1; 
            else d++;
        }
        cout<<maxd<<'\n';
    }
}

 G. Pair of Topics

Problem - 1324D - Codeforces

题意:寻找 i<j 并且 a[i]+a[j] > b[i]+b[j]的 ( i, j )对个数。

解法:令c[i] = a[i]-b[i],不等式转化为 i<j 并且c[i] > c[j],这样就好算多了。先把c数组排序,然后尺取或者二分查找解决。(代码里用的尺取)

#include <bits/stdc++.h>
using namespace std;
#define debug cout<<"!!!"<<endl;
#define int long long
const int N = 2e5+5;
int n, a[N], b[N], c[N], ans=0;

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin>>n;
    for(int i=1; i<=n; i++) cin>>a[i];
    for(int i=1; i<=n; i++) cin>>b[i];
    for(int i=1; i<=n; i++) c[i] = a[i]-b[i];
    sort(c+1,c+n+1);
    int l = 1, r = n;
    while(r > l){
        if(c[r] <= 0) break;
        if(c[l]+c[r] > 0) ans += r-l, r--;
        else l++;
    }
    cout<<ans;
}

H. Maximum White Subtree 

Problem - F - Codeforces

题意:求过每个点的子树中cnt1-cnt0的最大值。

解法:显然是个树形dp,选一个起点dp一次是O(N)。但是这里要求所有点dp值,所以要用到换根的树形dp,dp完第一个起点之后,要开始另一轮dfs,补上之前漏掉的——u对v的贡献。由于要避免重复,要先去掉f[v]对f[u]的贡献,再考虑加上这个贡献值,详见代码(另一轮dfs就是代码里的dfs2)

#include <bits/stdc++.h>
using namespace std;
#define debug cout<<"!!!"<<endl;
const int N = 2e5+5;
int n, a[N], f[N];
vector<int> g[N];

void dfs(int u,int fa){
    f[u] = (a[u]==1) ? 1 : -1;
    for(int v:g[u]){
        if(v==fa) continue;
        dfs(v,u);
        if(f[v]>0) f[u] += f[v];
    }
}
void dfs2(int u,int fa){
    for(int v:g[u]){
        if(v==fa) continue;
        int pre = f[u];
        if(f[v]>0) pre -= f[v]; //减去v的贡献
        if(pre>0) f[v] += pre; //计算出真正的f[v]
        dfs2(v,u); //下一层
    }
}
signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin>>n; for(int i=1; i<=n; i++) cin>>a[i];
    for(int i=1; i<=n-1; i++){
        int a,b; cin>>a>>b;
        g[a].push_back(b);
        g[b].push_back(a);
    }
    dfs(1,0);
    dfs2(1,0);
    for(int i=1; i<=n; i++) cout<<f[i]<<' ';
}

 I. Sleeping Schedule

Problem - E - Codeforces

解法:首先看到每次有两种选择,如果暴力做,复杂度将是2^n,不可行。但是我们发现,对于选了前i个数,sum值都是j的所有情况,前面的选择有相同的后效性。所以可以用 f [ i ][ j ]记录此时的good数,进行dp递推,这样就把复杂度降到了O(nh)。注意开始时f数组除f[0][0]外其他位置初始化为-inf,因为要避免没有到达过的状态进行非法递推。

补充:可以滚动数组把f[N][N]变成f[2][N],但没必要。

#include <bits/stdc++.h>
using namespace std;
#define debug cout<<"!!!"<<endl;
const int N = 2e3+5;
int n, h, l, r, a[N], f[N][N]; //f[i][j]表示第i天,sum为j的最大good数
signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin>>n>>h>>l>>r; for(int i=1; i<=n; i++) cin>>a[i];
    memset(f,-0x3f3f3f3f,sizeof(f)); f[0][0] = 0; //-inf表示不存在的状态
    for(int i=1; i<=n; i++)
    for(int j=0; j<h; j++){
        f[i][j] = max(f[i-1][(j-a[i]+h)%h], f[i-1][(j-(a[i]-1)+h)%h]);
        if(l<=j && j<=r) f[i][j]++;
    }
    int ans = 0;
    for(int j=0; j<h; j++) ans = max(ans, f[n][j]);
    cout<<ans;
}

J. CopyCopyCopyCopyCopy

Problem - B - Codeforces

解法:送分题。注意提取出的子序列是不用按顺序的,所以只要计算出不同数字的种数就可以了,用STL的哈希表或者数组桶都可以。

#include <bits/stdc++.h>
using namespace std;
#define debug cout<<"!!!"<<endl;
unordered_set<int> st;
int t, n, x;

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin>>t;
    while(t--){
        st.clear();
        cin>>n;
        for(int i=1; i<=n; i++) cin>>x, st.insert(x);
        cout<<st.size()<<'\n';
    }
}

K. Ehab and Path-etic MEXs

Problem - C - Codeforces (Unofficial mirror site, accelerated for Chinese users)

解法:思维+构造题,想到了就简单。分两种情况:如果图是一条链,那么最大MEX恒为n-1(取两头,中间路线肯定有0~n-2),随便构造都可以。如果不是链,也就是至少有一个点的度>=3,那么只要把边权0,1,2分给这个点的3条边,就可以保证最大MEX不超过2(因为从i到j不可能同时经过0,1,2三条边)。那最大MEX还能更小吗?不能。因为必定存在两点i,j路径上经过0,1两条边。

#include <bits/stdc++.h>
using namespace std;
#define debug cout<<"!!!"<<endl;
const int N = 1e5+5;
int n, a[N], b[N], du[N], ans[N];

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin>>n;
    for(int i=1; i<=n-1; i++){
        cin>>a[i]>>b[i]; du[a[i]]++; du[b[i]]++;
        ans[i] = -1;
    }
    int p=0;
    for(int i=1; i<=n; i++){
        if(du[i]>=3) {p=i;break;}
    }
    if(!p){
        for(int i=0; i<=n-2; i++) {cout<<i<<'\n';}
        return 0;
    }

    int l = 0, r = n-2;
    for(int i=1; i<=n-1; i++){
        if(a[i]==p || b[i]==p) cout<<l<<'\n', l++;
        else cout<<r<<'\n', r--;
    }
    
}

L. Yet Another Tetris Problem 

Problem - A - Codeforces

解法:送分题,所有数奇偶性相同就是YES,否则就是NO。

#include <bits/stdc++.h>
using namespace std;
#define debug cout<<"!!!"<<endl;
int t, n, x, y;

signed main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin>>t;
    while(t--){
        cin>>n>>x; bool flag = 1;
        for(int i=2; i<=n; i++) {cin>>y; if((x+y)%2) flag = 0;}
        puts(flag?"YES":"NO");
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值