• AcWing 每日一题


    本篇解题记录题源来自 AcWing 的 Summer 每日一题

    补题链接:Here

    2021/07/01 done

    Week 1

    星期一 AcWing 3485. 最大异或和 (Hard

    思路
    先求出前i个数的异或和sum[i],再在大小为m的滑动窗口内进行trie.

    QQ图片20210510213023.jpg

    • (mathcal{O}(nlog n))
    int n, m;
    const int N = 31e5 + 10;  //最多有n*31
    int p[N][35], ct[N], idx; //ct[n]的作用是标记滑动窗口内0,1的数量
    
    int sum[100010]; //sum[i]存前i个数的异或和
    void insertt(int u, int c) {
        int t = 0;
        for (int i = 30; i >= 0; i--) {
            int x = u >> i & 1;
            if (!p[t][x]) {
                p[t][x] = ++idx;
            }
            t = p[t][x];
            ct[t] += c; //标记这里(有或删除)一个数可以达到该位置
        }
    }
    int query(int u) {
        int t   = 0;
        int res = u;
        for (int i = 30; i >= 0; i--) {
            int x = u >> i & 1;
            if (ct[p[t][!x]] > 0) { //当x对面的那个数!x存在时(0,1)
                x = (x + 1) % 2;    //x就变成另外一个数 !x
            }
            res ^= x << i;
            t = p[t][x];
        }
        return res;
    }
    void solve() {
        cin >> n >> m;
        int t;
        for (int i = 1; i <= n; i++) {
            cin >> t;
            sum[i] = sum[i - 1] ^ t; //sum[i]表示前i个数的^
        }
        insertt(0, 1); //插入0,是为了方便前m个数进行异或得出的答案可以是它本身的值
        int res = 0;   //求最大值
        for (int i = 1; i <= n; i++) {
            if (i > m) insertt(sum[i - m - 1], -1); //将滑动窗口外的数除去,这时就要修改ct,故-1
            res = max(res, query(sum[i]));          //在滑动窗口内求最大值
            insertt(sum[i], 1);                     //求完后记得插入该值,方便后面的值进行异或
        }
        cout << res;
    }
    

    星期二 AcWing 3493. 最大的和 (Easy

    对于 (30\%) 数据,可以开两重 for 循环暴力找

    但在 (100\%) 数据里肯定会 T

    所以要用前缀和进行优化 (当然也可以用队列优化,这里就不展开了,贴一个讲解链接

    • (mathcal{O}(n))
    using ll    = long long;
    const int N = 1e5 + 10;
    ll a[N], s[N];
    ll st[N];
    void solve() {
        int n, k;
        cin >> n >> k;
        for (int i = 1; i <= n; ++i) cin >> a[i];
        ll sum = 0;
        for (int i = 1; i <= n; ++i) {
            cin >> st[i];
            if (st[i]) s[i] += s[i - 1], sum += a[i]; // 如果是 st[i] = 1 的情况说明直接就可选,sum 累加即可
            else
                s[i] = s[i - 1] + a[i]; // 需要转换状态的话则要利用前缀和累加了
        }
        ll res = 0;
        for (int i = k; i <= n; ++i) res = max(res, s[i] - s[i - k]);
        cout << res + sum;
    }
    

    星期三 AcWing 3499. 序列最大收益 (Mid

    参考最长上升子序列

    • f[i][j] 表示从0~i-1 中删j个点后,0~i 的收益最大值
      • 或者说成前i个点中删j个数,保留i的最大收益
    • 按i来划分集合,可以划分为0~i-1,其中0就代表前0个点删j个数,等价于不删.
    • f[i][j]可以从f[u][?]转移过来,我们对f[i][j]做一个转换
      • 既然要删j个数,可以先把后面[u+1,i-1]给删了,这样会删除i-1-u个数
      • 然后再在前面[0,u]中删j-(i-1-u)个数,此时u点和i点临近,可加贡献 w[a[u]][a[j]]
        故可以得到如下状态转移方程 f[i][j] = max(f[i][j],f[u][j-(i-u-1)]+w[a[u]][a[i]]);
    const int N = 210;
    int n, k, m;
    int a[N];	 // 记录元素
    int w[N][N]; // w[i][j]表示i->j的收益
    int f[N][N]; // f[i][j] 表示0~i-1中删除j个点后,0~i的最大收益
    void solve() {
    	cin >> n >> k >> m;
    	for (int i = 1; i <= m; ++i) cin >> a[i];
    	for (int i = 1; i <= n; ++i)
    		for (int j = 1; j <= n; ++j)
    			cin >> w[i][j];
    	memset (f, -0x3f, sizeof (f) );
    	f[1][0] = 0; // init
    	// 第1个点删0个数保留1的收益为0
    	for (int i = 1; i <= m; i ++ )
    		for (int j = 0; j <= k; j ++ )    //j从0~k,不删就是0
    			for (int u = 1; u < i; ++ u)  //最多删i-1个
    				if (j >= i - u - 1)       //注意是>=,=的条件下就说明既可以把后面删完,也可以直接分配到0~i中
    					//故要计算状态
    					f[i][j] = max (f[i][j], f[u][j - (i - u - 1)] + w[a[u]][a[i]]);
    	int ans = 0;
    	for (int i = 0; i <= k; ++i) ans = max (ans, f[m][i]);
    	cout << ans << '
    ';
    }
    

    星期四 AcWing 3502. 不同路径数

    这道题,爆搜 + set去重即可

    const int N = 10;
    int n, m, k;
    int e[N][N];
    set<int>S;
    int dx[4] = {0, 1, 0, -1};
    int dy[4] = {1, 0, -1, 0};
    int res = 0;
    void dfs(int sx, int sy, int u, int s) {
    	if (u == k) {
    		if (!S.count(s)) ++res, S.insert(s);
    		return;
    	}
    	for (int i = 0; i < 4; ++i) {
    		int x = sx + dx[i];
    		int y = sy + dy[i];
    		if (x < 1 || x > n || y < 1 || y > m)continue;
    		dfs(x, y, u + 1, s * 10 + e[x][y]);
    	}
    }
    void solve() {
    	cin >> n >> m >> k;
    	for (int i = 1; i <= n; ++i)
    		for (int j = 1; j <= m; ++j)
    			cin >> e[i][j];
    	for (int i = 1; i <= n; ++i)
    		for (int j = 1; j <= m; ++j) {
    			dfs(i, j, 0, e[i][j]);
    		}
    	cout << res;
    }
    

    星期五 AcWing 3510. 最长公共子序列 (Mid

    const int N = 1e6 + 7;
    int id[N], q[N];
    void solve() {
        int n;
        cin >> n;
        memset(id, -1, sizeof(id));
        for (int i = 0, x; i < n; ++i) {
            cin >> x;
            id[x] = i;
        }
        int tt = 0;
        q[0] = -1;
        for (int i = 0, x; i < n; ++i) {
            cin >> x;
            int k = id[x];
            if (k == -1)continue;
            int l = 0, r = tt;
            while (l  < r) {
                int mid = l + (r + 1) >> 1;
                if (q[mid] < k)l = mid;
                else r = mid - 1;
            }
            q[r + 1] = k;
            tt = max(tt, r + 1);
        }
        cout << tt << '
    ';
    }
    

    星期六 AcWing 3489. 星期几 (Easy)

    套公式,或者逐年累加

    int Day(int y, int m, int d) {
        if (m == 1 || m == 2)  m += 12, y -= 1;
        return (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400 +
                1) % 7;
    }
    string D[] = {
        "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
    };
    string DD[] = {
        "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
    };
    
    void solve() {
        int y, d, mm;
        string m;
        while ( cin >> d >> m >> y) {
            for (int i = 0; i < 12; ++i)
                if (m == D[i])cout << DD[(Day(y, i + 1, d))] << "
    ";
        }
    }
    

    星期日 AcWing 3481. 阶乘的和

    using ll = long long;
    int fact[20];//10!就大于1e6
    int combin[4100];//存所有阶乘能组合出来的数,0~10有11种阶乘,2^11空间存储
    int m = 0;
    void dfs(int u, int st) {
        if (u == 11) {
            int res = 0;
            for (int i = 0; i < 11; ++i) {
                if (st >> i & 1) {
                    res += fact[i];
                }
            }
            combin[m++] = res;
            return;
        }
        dfs(u + 1, st);
        dfs(u + 1, st | 1 << u);
    }
    void solve() {
        fact[0] = 1;
        for (int i = 1; i <= 10; ++i) fact[i] = fact[i - 1] * i;
        dfs(0, 0);
        sort(combin, combin + m);
        //  m = unique(combin, combin + m) - combin; // 去重,可有可无
        int x;
        while (cin >> x, x >= 0) {
            int l = 1, r = m - 1;
            int mid = l + r >> 1;
            //二分查找预处理数组
            while (l < r) {
                mid = l + r >> 1;
                if (combin[mid] >= x) r = mid;
                else l = mid + 1;
            }
            cout << (combin[l] != x ? "NO
    " : "YES
    ");
        }
    }
    

    Week 2

    星期一 AcWing 3516. 最大面积 (Hard

    第一眼看过去像二维前缀和问题,但实际要使用单调栈解决

    下面给出 4 道相关知识点递增的题目

    830.单调栈 -> 131. 直方图中最大的矩形 -> 152. 城市游戏 -> 3516. 最大面积


    Y总视频讲解,建议直接空降 30 min

    const int N = 2010;
    char g[N][N];
    int l[N], r[N], q[N];
    int U[N], D[N], L[N], R[N];
    int s[N][N];
    int n, m;
    
    int calc(int h[], int n) {
    	h[0] = h[n + 1] = -1;
    	int tt = 0;
    	q[0] = 0;
    	for (int i = 1; i <= n; i ++ ) {
    		while (h[q[tt]] >= h[i]) tt -- ;
    		l[i] = q[tt];
    		q[ ++ tt] = i;
    	}
    	tt = 0;
    	q[0] = n + 1;
    	for (int i = n; i; i -- ) {
    		while (h[q[tt]] >= h[i]) tt -- ;
    		r[i] = q[tt];
    		q[ ++ tt] = i;
    	}
    	int res = 0;
    	for (int i = 1; i <= n; i ++ )
    		res = max(res, h[i] * (r[i] - l[i] - 1));
    	return res;
    }
    void init() { // 注意初始化别写错...
    	for (int i = 1; i <= n; ++i) { // 枚举行
    		for (int j = 1; j <= m; ++j)
    			if (g[i][j] == '1')s[i][j] = s[i - 1][j] + 1;
    			else s[i][j] = 0;
    		U[i] = max(U[i - 1], calc(s[i], m));
    	}
    	memset(s, 0, sizeof(s));
    	for (int i = n; i; i--) { // 枚举行
    		for (int j = 1; j <= m; ++j)
    			if (g[i][j] == '1')s[i][j] = s[i + 1][j] + 1;
    			else s[i][j] = 0;
    		D[i] = max(D[i + 1], calc(s[i], m));
    	}
    	memset(s, 0, sizeof(s));
    	for (int i = 1; i <= m; i ++ ) { // 枚举列
    		for (int j = 1; j <= n; j ++ )
    			if (g[j][i] == '1') s[i][j] = s[i - 1][j] + 1;
    			else s[i][j] = 0;
    		L[i] = max(L[i - 1], calc(s[i], n));
    	}
    	memset(s, 0, sizeof(s));
    	for (int i = m; i; i -- ) { // 枚举列
    		for (int j = 1; j <= n; j ++ )
    			if (g[j][i] == '1') s[i][j] = s[i + 1][j] + 1;
    			else s[i][j] = 0;
    		R[i] = max(R[i + 1], calc(s[i], n));
    	}
    }
    void solve() {
    	cin >> n >> m;
    	for (int i = 1; i <= n; ++i)scanf("%s", g[i] + 1);
    	init();
    	int _;
    	cin >> _;
    	while (_--) {
    		int x, y;
    		cin >> x >> y, x++, y++;
    		cout << max(max(U[x - 1], D[x + 1]), max(L[y - 1], R[y + 1])) << "
    ";
    	}
    }
    

    星期二 AcWing 3404. 谁是你的潜在朋友

    按题意来,把看同一本书的人放进数组。

    意外的简单

    void solve() {
        int n, m;
        cin >> n >> m;
        bool vis[n + 1] = {false};
        int b[n + 1];
        vector<int>a[m + 1];
        for (int i = 1; i <= n; ++i) {
            cin >> b[i];
            a[b[i]].push_back(i);
        }
        for (int i = 1; i <= n; ++i) {
            if (a[b[i]].size() == 1)cout << "BeiJu
    ";
            else cout << a[b[i]].size() - 1 << "
    ";
        }
    }
    

    星期三 AcWing 3483. 2的幂次方

    这道题,本质是对在二进制下的1进行递归处理

    int n;
    string dfs(int n) {
        string str;
        for (int i = 20; i >= 0; --i) {
            int f = 1 << i;
            if (i > 2 and (n & f)) {
                str += "2(";
                str += dfs(i);
                str += ')';
                n -= f;
                if (n != 0) str += '+';
            } else if (i <= 2 and (n & f)) {
                if (i == 0)str += "2(0)";
                if (i == 1) str += "2";
                if (i == 2) str += "2(2)";
                n -= f;
                if (n != 0) str += '+';
            }
        }
        return str;
    }
    void solve() { while (cin >> n)cout << dfs(n) << "
    ";}
    
    // 简化代码
    string dfs(int x) {
        if (!x)return "0";
        string ans = "";
        for (int j = 31; j >= 0; --j) {
            if ((x >> j) & 1) ans += (j == 1) ? "2+" : "2(" + dfs(j) + ")+";
        }
        return ans.substr(0, ans.size() - 1);
    }
    void solve() {
        int n;
        while (cin >> n)cout << dfs(n) << "
    ";
    }
    

    星期五 AcWing 3333. K-优字符串

    int Case = 1;
    void solve() {
    	int n, k, sum = 0;
    	cin >> n >> k;
    	string s;
    	cin >> s;
    	int l = 0, r = s.size() - 1;
    	while (l < r) {
    		if (s[l] != s[r])sum++;
    		l++, r--;
    	}
    	cout << "Case #" << Case++ << ": " << abs(sum - k) << "
    ";
    }
    

    Week 3

    星期一 AcWing 3554. 二进制

    解法一: 模拟,二进制累加,当存在溢出时,即最后 c = 1 时要多输出一个 1

    void solve() {
        string a, b;
        cin >> a, b = a;
        int c = 0;
        for (int i = 31; i >= 0; i -= 1) {
            int tmp  = (a[i] - '0') + c;
            if (i == 31)tmp++;
            a[i] = (tmp % 2) + '0';
            c = tmp / 2;
        }
        if (c) cout << c;
        cout << a << "
    ";
        c = 0;
        for (int i = 31; i >= 0; i -= 1) {
            int tmp = (b[i] - '0') + c;
            if (i == 31 || i == 30)tmp++;
            b[i] = (tmp % 2) + '0';
            c = tmp / 2;
        }
        if (c)cout << c;
        cout << b << "
    ";
    }
    

    解法二:转换成 long long 计算

    using ll = long long;
    void f(ll n) {	
        if (n >> 32 & 1)cout << 1;//最大的32位加3会变成33位,检查一下	
        for (int i = 31; i >= 0; i -= 1) {		
            if (n >> i & 1)cout << 1;		
            else cout << 0;	
        }	
        cout << "
    ";
    }
    void solve() {	
        string s; cin >> s;	
        ll n = 0;	
        for (int i = 0; i < 32; ++i)		
            if (s[i] == '1')  
                n = n | (1ll << (31 - i));	
        f(n + 1), f(n + 3);
    }
    

    解法三:Python 不讲武德

    T = int(input())for _ in range(T):    
        n = int(input(),2)    
        print(bin(n+1)[2:].rjust(32, '0'))    
        print(bin(n+3)[2:].rjust(32, '0'))
    

    星期二 AcWing 3565. 完美矩阵

    using ll = long long;
    const int N = 110;
    ll a[N][N];
    vector<int>e[N][N];
    void solve() {
        int n, m; cin >> n >> m;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j)e[i][j].clear();
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j) {
                cin >> a[i][j];
                e[min(i, n - i + 1)][min(j, m - j + 1)].push_back(a[i][j]);
            }
    
        ll ans = 0;
    
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j) {
                if (!e[i][j].size())continue;
                sort(e[i][j].begin(), e[i][j].end());
                int t = e[i][j][(e[i][j].size() + 1) / 2 - 1];
                for (int k = 0; k < e[i][j].size(); ++k)
                    ans += abs(t - e[i][j][k]);
            }
        cout << ans << "
    ";
    }
    

    星期三 AcWing 3574. 乘积数量

    方案一:前缀和维护区间负数个数

    看到求区间的问题,会想到前缀和优化

    我们可以用前缀和维护 ([1,s_n]) 区间内的负数个数(因为会影响乘积的正负号的只有负数)

    则对于区间 ([s_l,s_r]=v),若 (v) 是奇数,则表示该区间的乘积为负数,反之是正数

    • (s_r−s_{l−1})为奇数,则必定是 (s_r)(s_{l−1}) 一奇一偶

    • (s_r−s_{l−1})为偶数,则必定是 (s_r)(s_{l−1}) 都是偶数或都是奇数

    于是我们可以从前往后便利一遍数组,求出前缀和分别为奇数和偶数的个数

    然后是一个组合数的问题了,设前缀和为奇数的个数为 (v_1) ,前缀和为偶数的个数为 (v_2)

    区间为正数的子区间个数为 (C^1_{v1}+C^1_{v2})

    区间为负数的子区间个数为 (C^1_{v1}×C^1_{v2})

    int n;
    int s[N];
    int s0, s1;
    void solve() {    
        cin >> n;    
        s0 ++ ;    
        for (int i = 1; i <= n; ++ i) {        
            int x;        
            cin >> x;        
            s[i] = s[i - 1] + (x < 0);        
            if (s[i] & 1) ++ s1;        
            else ++ s0;    
        }    
        cout << (LL)s0 * s1 << " " << (LL)s0 * (s0 - 1) / 2 + (LL)s1 * (s1 - 1) / 2 << endl;
    }
    

    方案二:DP

    DP[i] 表示以 i 结尾的区间有多少个含奇数个负数

    • 则若 xs[i] < 0,则接到 i - 1 含偶数个负数的区间上
    • 否则,则接到 i - 1 含奇数个负数的区间上

    但容易发现 (dp_i) 仅和前一项相关,所以采用滚动数组

    const int N = 2e5 + 10;
    int a[N], c[2];
    ll cnt0, cnt1;
    void solve() {
        int n;  cin >> n;
        for (int i = 1; i <= n; ++i)
            cin >> a[i];
        int v = 1;
        c[v]++;
        for (int i = 1; i <= n; ++i) {
            if (a[i] < 0) v ^= 1;
            cnt0 += (ll)c[v];
            cnt1 += (ll)c[v ^ 1];
            c[v]++;
        }
        cout << cnt1 << " " << cnt0 << '
    ';
    }
    

    星期四 AcWing 3580. 整数配对

    没什么好讲的,排序,累加即可

    void solve() {
        int n; cin >> n;
        int a[n + 1], cnt = 0;
        for (int i = 1; i <= n; ++i)cin >> a[i];
        sort(a + 1, a + 1 + n);
        for (int i = 2; i <= n; i += 2)cnt += a[i] - a[i - 1];
        cout << cnt ;
    }
    

    星期五 AcWing 3583. 整数分组

    先对数组按升序排序,然后记录每个数符合条件的最左侧点下标,然后再用DP优化

    • (f(i,j) = max(f(i,j),f(L_i-1,j-1) + i - L[i] + 1))

    输出 (f[n][k]) 即可

    const int N = 5e3 + 10;
    int f[N][N], n, a[N], L[N], k;
    void solve() {
        cin >> n >> k;
        for (int i = 1; i <= n; ++i)cin >> a[i];
        sort(a + 1, a + 1 + n);
        for (int i =  1; i <= n; ++i) {
            int p = i;
            while (p > 1 && a[p - 1] >= a[i] - 5) p--;
            L[i] = p;
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= k; ++j)f[i][j] = f[i - 1][j];
            for (int j = 1; j <= k; ++j)
                f[i][j] = max(f[i][j], f[L[i] - 1][j - 1] + i - L[i] + 1);
        }
        cout << f[n][k] << "
    ";
    }
    

    Week 4

    星期三 AcWing 3617. 子矩形计数

    简单的前缀和累加

    using ll = long long;
    ll a[40001], b[40001], n, m, i, j, k, ans = 0;
    void solve() {
        cin >> n >> m >> k;
        for (i = 0; i < n; i++) {cin >> a[i]; if (a[i]) a[i] += a[i - 1];}
        for (i = 0; i < m; i++) {cin >> b[i]; if (b[i]) b[i] += b[i - 1];}
        for (i = 1; i <= n; i++)
            if (k % i == 0) {
                ll u = i, v = k / i, x = 0, y = 0;
                for (j = 0; j < n; j++) if (a[j] >= u) x++;
                for (j = 0; j < m; j++) if (b[j] >= v) y++;
                ans += x * y;
            }
        cout << ans;
    }
    

    星期四 AcWing 3624. 三值字符串

    利用双指针维护最小长度

    const int inf = 0x3f3f3f3f;
    void solve() {
        string s; cin >> s;
        int cnt[4] = {0};
        int l = 0, ans = inf;
        for (int i = 0; i < (int)s.size(); ++i) {
            cnt[s[i] - '0']++;
            while (cnt[1] and cnt[2] and cnt[3]) {
                cnt[s[l] - '0']--;
                ans = min(ans, i - l + 1);
                ++l;
            }
        }
        cout << (ans == inf ? 0 : ans) << "
    ";
    }
    

    星期五 AcWing 3629. 同心圆涂色

    模拟即可,注意 PI 的取值

    const double pi = 3.1415926535897932384626433832795;
    bool cmp(int a, int b) {return a > b;}
    void solve() {
        int n; cin >> n;
        int r[n];
        for (int i = 0; i < n; ++i)cin >> r[i];
        sort(r, r + n, cmp);
        double ans = 0.0;
        int i;
        for (i = 1; i < n; i += 2) {
            ans += pi * (r[i - 1] * r[i - 1] - r[i] * r[i]);
        }
        if (i == n)ans += pi * r[i - 1] * r[i - 1];
        printf("%.06lf", ans);
    }
    

    Week 5

    星期一 AcWing 3636. 数组延伸

    const int N = 1e5 + 10, mod = 1e9 + 7;
    ll n, x;
    ll a[N];
    void solve() {
        cin >> n >> x;
        for (int i = 1; i <= n; ++i)cin >> a[i];
        ll sum = 0, psum = 0;
        //分别代表一开始的数组和,可以向外扩展最小次数的那个数之前的和
        int cnt = N;
        //最小向外拓展次数
        for (int i = 1; i <= n; ++i) {
            sum += a[i];
            int c = 0;
            for (int j = a[i]; j % x == 0; j /= x)c++;
            if (c < cnt)cnt = c, psum = sum - a[i];
        }
        //答案就是一开始的数组和加上向外拓展次数*数组和,再加上截止到向外拓展次数最小的那个数之前的和(到这个数开始,就不是完整的拓展了)
        cout << sum * (cnt + 1) + psum << endl;
    }
    

    星期二 AcWing 3646. 分水果

    最少 (0,0,1) 、(0,1,0)、(1,0,0),至多 (1,1,1)

    所以特判情况即可

    void solve() {
        int a[3];
        for (int i = 0; i < 3; ++i)cin >> a[i];
        int ans = 0;
        if (a[0])ans++, a[0]--;
        if (a[1])ans++, a[1]--;
        if (a[2])ans++, a[2]--;
    
        sort(a, a + 3);
        if (a[2] and a[1])ans++, a[2]--, a[1]--;
        if (a[2] and a[0])ans++, a[2]--, a[0]--;
        if (a[0] and a[1])ans++, a[0]--, a[1]--;
    
        if (a[0] and a[1] and a[2])ans++;
        cout << ans << "
    ";
    }
    

    星期三 AcWing 3655. 楼层

    数学 or 模拟

    void solve() {
        int n, x;
        cin >> n >> x;
        if (n <= 2)cout << 1 << "
    ";
        else cout << (n - 2 + x - 1) / x + 1 << "
    ";
        // cout << ((n-3)/x+2) << endl;
    }
    

    星期五 AcWing 3664. 数组补全

    贪心

    找比y小的个数,一定不能超过中位数位置,然后左置1,右置y

    void solve() {
        int n, k, p, x, y, tot = 0, a, d = 0, i = 0, l, m;
        cin >> n >> k >> p >> x >> y;
        for (; i < k; tot += a, d += (a < y), i++)cin >> a;
        l = n / 2 - d < n - k ? n / 2 - d : n - k;
        m = n - k - l;
        if (l < 0 || tot + l + m * y > x)cout << -1;
        else {
            while (m--)cout << y << ' ';
            while (l--)cout << 1 << ' ';
        }
    }
    

    Week 6

    星期一 AcWing 3672. 数组重排

    i - a[i] != j - a[j]
    等价于 i - j != a[i] - a[j]
    只需要逆序排列即可

    void solve() {
        int n; cin >> n;
        vector<int>a(n);
        for (int &x : a)cin >> x;
        sort(a.begin(), a.end(), greater<int>());
        for (int i = 0; i < n; ++i)cout << a[i] << " ";
        cout << '
    ';
    }
    

    星期二 AcWing 3679. 素数矩阵

    4 1 0 0 0 0 0 
    0 4 1 0 0 0 0 
    0 0 4 1 0 0 0
    0 0 0 4 1 0 0 
    0 0 0 0 4 1 0
    0 0 0 0 0 4 1
    1 0 0 0 0 0 4 
    
    void solve() {
        int n; cin >> n;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                if (i == j)cout << 4 << " ";
                else if (j == i + 1) cout << 1 << " " ;
                else if (i == n - 1 && j == 0) cout << 1 << " ";
                else cout << 0 << " ";
            }
            cout << endl;
        }
    }
    

    星期三 AcWing 3686. 移动序列

    定位区间,然后计算1,0个数

    void solve() {
        int n;
        cin >> n;
        vector<int>a(n);
        int cnt1 = 0;
        for (int &x : a) {
            cin >> x;
            if (x == 1)cnt1++;
        }
        int i = 0, j = n - 1;
        while (a[i] == 0) i++; //第一个1的位置
        while (a[j] == 0) j--; //最后一个1的位置
        cout << j - i + 1 - cnt1 << '
    ';
    }
    

    星期四 AcWing 3697. 回文子序列

    注意:问的是回文子序列,不是子串。

    长度大于等于3的回文子序列 等价于 两个相等的元素位置之差大于1

    所以:遍历数组,找出两个相等的元素。如果位置之差大于1就输出 YES。

    如果遍历完数组,也没找到,就输出 NO。

    当然也可以用哈希表记录下元素出现的位置,在 (O(N)) 时间内过掉。这个方法可以看看其他题解。

    void solve() {
        int n; cin >> n;
        vector<int>a(n);
        for (int &x : a)cin >> x;
        bool f = false;
        for (int i = 0; i < n and !f; ++i)
            for (int j = i + 1; j < n; ++j)
                if (a[i] == a[j] and j - i > 1) f = true;
        cout << (f ? "YES
    " : "NO
    ");
    }
    

    Week 7

    星期一 AcWing 3705. 子集mex值

    通过维护两个变量从0开始,如果有0、1、2、3...这样的直接慢慢向上叠加

    const int N = 1e5 + 100;
    ll n, a[N];
    void solve() {
        cin >> n;
        for (int i = 0; i < n; ++i)cin >> a[i];
        sort(a, a + n);
        ll m = 0, k = 0;
        for (int i = 0; i < n; ++i) {
            if (a[i] == m)m++;
            else if (a[i] == k)k++;
        }
        cout << m + k << endl;
    }
    

    星期二 AcWing 3711. 方格涂色

    待补

    const int N = 1e5+10;
    int main(){
        int t; scanf("%d", &t);
        while (t--){
            int n, u, r, d, l;
            scanf("%d %d %d %d %d", &n, &u, &r, &d, &l);
            int top, bottom, right ,left;
            bool f = 0;
            top = bottom = right = left = 0;
            
    		if(u == n) top = 2;
            else if(u == n-1) top = 1;
            
            if(r == n) right = 2;
            else if(r == n-1) right = 1;
    
            if(d == n) bottom = 2;
            else if(d == n-1) bottom = 1;
    
            if(l == n) left = 2;
            else if(l == n-1) left = 1;
            
    //        printf("%d %d %d %d
    ",top, bottom, right, left);
    
            for(int i = 0; i < 16; i++){
            	int a[4] = {0,0,0,0}, cnt = 0;
            	while(i>>cnt){
            		a[cnt] = (i>>cnt)&1;
            		cnt++;
    			}
    			if(a[0]+a[1] >= top && a[0]+a[1] <= u) 
    			if(a[0]+a[2] >= left && a[0]+a[2] <= l)
    			if(a[1]+a[3] >= right && a[1]+a[3] <= r)
    			if(a[2]+a[3] >= bottom && a[2]+a[3] <= d)
    			f = 1;
    			
    		}
    
            if(f) cout << "YES
    ";
            else cout << "NO
    ";
        }
    }
    

    星期三 AcWing 3720. 数组重排

    因为对于每个 ai+bi 都要满足 <=x 这个条件

    我们只需要贪心的 使得每个和尽可能的小即可

    所以对两个数组 按升序 和 降序 sort 一下即可

    void solve() {
        int n, x;
        cin >> n >> x;
        vector<int> A(n, 0), B(n, 0);
        for (int i = 0; i < n; ++i)
            cin >> A[i];
        for (int i = 0; i < n; ++i)
            cin >> B[i];
        sort(A.begin(), A.end());
        sort(B.begin(), B.end(), greater<int>());
        bool ok = true;
        for (int i = 0; i < n; ++i)
            if (A[i] + B[i] > x)
                ok = false;
        cout << (ok ? "Yes" : "No") << endl;
    }
    

    星期四 AcWing 3725. 卖罐头

    math

    ll _, n, x;
    void solve() {
        ll l, r;
        cin >> l >> r;
        ll a = r + 1;
        if (l % a >= (a + 1) / 2) cout << "YES
    ";
        else cout << "NO
    ";
    }
    

    星期五 AcWing 3729. 改变数组元素

    int arr[200001];
    int res[200001];
    void solve() {
        int n, i, mn = 1e9;
        cin >> n;
        for (i = 1; i <= n; i++) cin >> arr[i];
        for (i = n; i >= 1; i--) {
            mn = min(mn, i - arr[i]);
            res[i] = (mn < i);
        }
        for (i = 1; i <= n; i++) cout << res[i] << " ";
        cout << endl;
    }
    

    Week 8

    星期一 AcWing 3730. 寻找序列

    由于题目保证 (a_i ot= b_i ot = c_i)所以直接逐位判断选什么即可,时间复杂度 (mathcal{O}(n))

    void solve() {
        int n;
        cin >> n;
        vector<int> a(n);
        for (int i = 0; i < n; i++)  cin >> a[i];
        vector<int> b(n);
        for (int i = 0; i < n; i++)  cin >> b[i];
        vector<int> c(n);
        for (int i = 0; i < n; i++)  cin >> c[i];
        vector<int> p(n, -1);
        for (int i = 0; i < n; i++) {
            p[i] = a[i];
            if (p[i] == p[(i + 1) % n] || p[i] == p[(i + n - 1) % n]) {
                p[i] = b[i];
                if (p[i] == p[(i + 1) % n] || p[i] == p[(i + n - 1) % n]) {
                    p[i] = c[i];
                }
            }
        }
        for (int i = 0; i < n; i++) {
            if (i > 0) cout << " ";
            cout << p[i];
        }
        cout << '
    ';
    }
    

    星期二 AcWing 3731. 序列凑零

    ll _, n;
    void solve() {
        cin >> n;
        int a[n + 1];
        for (int i = 0; i < n; ++i) cin >> a[i];
        for (int i = 0; i < n; i += 2) cout << a[i + 1] << ' ' << -a[i] << ' ';
        cout << endl;
    }
    

    星期三 AcWing 3732. 矩阵复原

    #include <bits/stdc++.h>
    #define x first
    #define y second
    using namespace std;
    using ll = long long;
    typedef pair<int, int> PII;
    const int N = 510, M = N * N;
    int n, m, k;
    int g[N][N];
    PII pos[M];
    void solve() {
        cin >> n >> m;
        for (int i = 1; i <= n; ++ i) {
            for (int j = 1; j <= m; ++ j) {
                int x;
                cin >> x;
                pos[x].y = j;
            }
        }
        for (int i = 1; i <= m; ++ i) {
            for (int j = 1; j <= n; ++ j) {
                int x;
                cin >> x;
                pos[x].x = j;
            }
        }
        for (int i = 1; i <= n * m; ++ i) {
            auto &p = pos[i];
            g[p.x][p.y] = i;
        }
        for (int i = 1; i <= n; ++ i) {
            for (int j = 1; j <= m; ++ j) {
                cout << g[i][j] << " ";
            }
            cout << endl;
        }
    
    }
    int main() {
        ios::sync_with_stdio(false), cin.tie(nullptr);
        int _; for (cin >> _; _--;) solve();
        return 0;
    }
    

    The desire of his soul is the prophecy of his fate
    你灵魂的欲望,是你命运的先知。

  • 相关阅读:
    1111实验二 作业调度模拟实验
    1006实验一实验报告
    0909对操作系统的认识
    南阳OJ-138 找球号(二)(hash表应用)
    南阳OJ-38 布线问题(最小生成树应用_prim)
    插入排序
    南阳OJ-756 重建二叉树(二叉树的中序遍历和后序遍历求先序遍历)
    南阳OJ-63 小猴子下落(数据结构-二叉树)
    UVA OJ-11095 Maximum Product(暴力求解法)
    UVA OJ-725 Division (暴力求解法)
  • 原文地址:https://www.cnblogs.com/RioTian/p/14761145.html
Copyright © 2020-2023  润新知