iwtgm-35

发布时间 2023-12-13 22:47:40作者: WW爆米花

Codeforces Round 913 (Div. 3)

A.

把给定坐标的同一行同一列的每个数都输出(除本身)

void solve() {
    char c;
    int d;
    cin>>c>>d;
    for(int i=1;i<=8;i++){
        if(i==d)continue;
        cout<<c<<i<<endl;
    }
    for(int i=0;i<=7;i++){
        char tmp=(char)('a'+i);
        if(tmp==c)continue;
        cout<<tmp<<d<<endl;
    }
}

B.

字符串模拟,用deque较为方便,大小写字母分开存(同时存下下标)
最后合并放入vector排序,输出

deque<pair<int,char>>low,up;
void solve() {
    string s;cin>>s;
    for(int i=0;i<s.size();i++){
        if(s[i]=='b'){
            if(low.size()){
                low.pop_back();
            }
        }else if(s[i]=='B'){
            if(up.size()){
                up.pop_back();
            }
        }else if(s[i]>='a'&&s[i]<='z'){
            low.push_back({i,s[i]});
        }else if(s[i]>='A'&&s[i]<='Z'){
            up.push_back({i,s[i]});
        }
    }
    vector<pair<int,char>>v;
    while(low.size()){
        v.push_back(low.front());low.pop_front();
    }
    while(up.size()){
        v.push_back(up.front());up.pop_front();
    }
    sort(v.begin(),v.end());
    for(int i=0;i<v.size();i++){
        cout<<v[i].second;
    }
    cout<<endl;
}

C.

不同的可以两两消除,那么我们关心相同数量最多的
如果最大数比其他的数量都多,那么一定会剩下最大数-其他数量
若小等于其他数量,因为相同数最大的已经挑出来了,其他相同也可以与最大抵消,可以优先让其他数先两两抵消
奇数一定会剩下一个(因为两两消除)

void solve() {
    int n;cin>>n;
    string s;cin>>s;
    vector<int>v(30);
    int ma=-inf;
    for(int i=0;i<s.size();i++){
        int tmp=s[i]-'a';
        v[tmp]++;
        ma=max(ma,v[tmp]);
    }
    int other=s.size()-ma;
    if(ma<=other){
        if(s.size()%2==0){
            cout<<0<<endl;
            return ;
        }else {
            cout<<1<<endl;
            return ;
        }
    }else{
        cout<<ma-other<<endl;
    }
}

D.

二分答案,二分确实是个好东西
重点是更新区间,往左跳、往右跳,和线段端点取小

int n, l[N], r[N];
 
bool check(int k) {
    int L = 0, R = 0;
    for (int i = 1; i <= n; i++) {
        int jump_l = L - k, jump_r = R + k;
        if (jump_l > r[i] || jump_r < l[i]) return false;
        L = max(jump_l, l[i]), R = min(jump_r, r[i]);
    }
    return true;
}
 
void solve() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> l[i] >> r[i];
    }
    int L = 0, R = 1e9, ans = -1;
    while (L <= R) {
        int mid = (L + R) / 2;
        if (check(mid)) {
            ans = mid;
            R = mid - 1;
        } else {
            L = mid + 1;
        }
    }
    cout << ans << endl;
}

E.

首先每一位不能有进位
那么每一位对应的三个数相加等于n的对应位的数
数字的每一位都是独立的,用乘法原理

#define int long long
map<int,int>mp;
void solve() {
    for(int i=0;i<=9;i++){
        for(int j=0;j<=9;j++){
            for(int k=0;k<=9;k++){
                mp[i+j+k]++;
            }
        }
    }
    int t;cin>>t;
    while(t--){
        int n;cin>>n;
        ll ans=1;
        while(n){
            int tmp=n%10;
            ans*=mp[tmp];
            n/=10;
        }
        cout<<ans<<endl;
    }
}

F.

可知两种操作都不会改变元素的相对顺序
把它看成一个环,若存在一个起点和一个终点,起点到终点是非递减的,并且囊括了所有点,那么这个序列是可行的
现在考虑操作次数
分类讨论顺逆时针
顺时针:
一种方法:就是把起点及以后的点都放到前面去,操作次数就是起点及起点以后的点的个数
另一种方法:先翻转,把终点及终点后的数都放到前面,最后再翻转一次
取两种方法的操作次数的最小值
逆时针:
可以先翻转成顺时针,按照顺时针的方法做
区别在于第一种方法要加上翻转的一次操作
第二种方法只+1,(在顺时针的时候+2),因为最开始已经+1
可以手玩模拟一个样例就能得知

void solve() {
    int n,t,q;
    cin>>n;
    vector<int>a(n);
    for(auto &i:a)cin>>i;
    t=*min_element(a.begin(),a.end());
    q=*max_element(a.begin(),a.end());
    int res=inf;
    for(int i=0,f=1;i<n;i++){
        if(a[i]!=t)continue;
        if(a[(i-1+n)%n]!=q)continue;
        f=1;
        for(int j=1;j<n&&f;j++){
            if(a[(i+j)%n]>=a[(i+j-1)%n])continue;
            f=0;
        }
        if(f)res=min(res,min((n-i)%n,i+2));
        break;
    }
    reverse(a.begin(),a.end());
    for(int i=0,f;i<n;i++){
        if(a[i]!=t)continue;
        if(a[(i-1+n)%n]!=q)continue;
        f=1;
        for(int j=1;j<n&&f;j++){
            if(a[(i+j)%n]>=a[(i+j-1)%n])continue;
            f=0;
        }
        if(f)res=min(res,min(i+1,(n-i)%n+1));
        break;
    }
    if(res==inf){
        cout<<-1<<endl;
    }
    else cout<<res<<endl;
}

G.

改变第 i 盏灯的状态同时也会改变第 i盏灯的状态,所以尝试从 i向 ai 连一条有向边
n个点,n条边,基环树
对于那些状态为1且入度为0的点u,必须要对第u盏灯进行一次操作将其状态变为0
因此可以先对基环树进行拓扑排序,把挂在环上的链先处理完,最后再处理环。
遍历环上的每一个点并统计状态为1的点的数量,如果这个数量是奇数那么无解(因为每次都会同时改变两盏灯的状态,因此状态为1的点的数量也会改变偶数次,即永远不会变成偶数)。否则一定可以将环上点的状态都变成0.
容易知道在任意方案中每个点要么被改变一次,要么不改变。因此只需选择环中的一个点,对两种情况都模拟一边,选择改变次数最小的方案即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
string s;
int ne[N], deg[N];//与i相连的一条边的另一点,入度
queue<int>q;
void solve() {
    int n;
    cin>>n>>s;s=" "+s;
    memset(deg, 0, n + 10 << 2);
    for (int i = 1; i <= n; i++) {
        int x;
        cin>>x;
        ne[i] = x;
        deg[x]++;
    }
    for (int i = 1; i <= n; i++) {//拓扑
        if (!deg[i]) q.push(i);
    }
    vector<int> ans;
    while (q.size()) {
        int u = q.front();q.pop();
        if (s[u] & 1) {
            s[u] ^= 1;
            s[ne[u]] ^= 1;
            ans.push_back(u);
        }
        if (--deg[ne[u]] == 0) q.push(ne[u]);
    }
    for (int i = 1; i <= n; i++) {
        if (deg[i]) {//环上
            int u = i, cnt = 0, sum = 0;
            while (deg[u]) {
                deg[u] = 0;
                cnt++;    // 统计环中点的数量
                sum += s[u] & 1;    // 统计状态为1的点的数量
                u = ne[u];
            }
            if (sum & 1) {    // sum是奇数则无解
                printf("-1\n");
                return;
            }
            vector<int> p1, p2;
            u = i;    // 任选环中一个点
            for (int j = 0, t = 0; j < cnt; j++) {    // 这个点改变一次
                if (!j || (t ^ s[u]) & 1) {//一条边的两个点都是1,只需改变第一个点
                    t = 1;                 //若一条边的1个点是0,一个点是1,则要改变2次
                    p1.push_back(u);
                }
                else {
                    t = 0;
                }
                u = ne[u];
            }
            u = i;
            for (int j = 0, t = 0; j < cnt; j++) {    // 这个点不改变
                if (j && (t ^ s[u]) & 1) {
                    t = 1;
                    p2.push_back(u);
                }
                else {
                    t = 0;
                }
                u = ne[u];
            }
            if (p1.size() > p2.size()) p1.swap(p2);    // 选择改变次数最小的方案
            for (auto &x : p1) {
                ans.push_back(x);
            }
        }
    }
    printf("%d\n", ans.size());
    for (auto &x : ans) {
        printf("%d ", x);
    }
    printf("\n");
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        solve();
    }

    return 0;
}