Codeforces AIM Tech Round 4 (Div. 2) 总结

这场比赛打完之后…心态爆炸…熬夜打比赛打到凌晨3:00…比赛中A掉了4道题…结果system test测完后…C题瞎糊的算法T掉了,D题因为某个小细节…炸掉了…结果只有1600+名…
rating-=100
目前rating:1720…(上次刚打上去这次掉下来,好憋屈qwq)

A. Diversity

题意:给你一串只由小写字母组成的字符串,问你最少改几个字母(随意替换),可以使这个字符串有不少于 k 个不同的字符,如果无论怎么改都不可能做到,输出impossible
思路&&题解:首先,很显然,如果k大于字符串长度,是绝对不可能做到的,直接输出 impossible 就行了。其余情况只要扫一遍字符串长度,统计一下已经有几个不一样的字符 tot ,之后输出 max(0,ktot) 就行了。

代码如下:

#include<bits/stdc++.h>
using namespace std;
bool book[26];
int tot=0;
string s;
int k;
int main() {
    cin>>s>>k;
    if(s.length()<k) {
        puts("impossible");
        return 0;
    }
    for(int i=0;i<s.length();i++) {
        if(!book[s[i]-'a']) {
            book[s[i]-'a']=1;
            tot++;
        }
    }
    if(tot>=k) {
        puts("0");
        return 0;
    }
    else {
        printf("%d\n",k-tot);
        return 0;
    }
    return 0;
} 

B. Rectangles

题意:给你一个 n×m 01 矩阵,其中每个元素都由 0 或者1组成,要求你在其中找出非空子集,使它满足下面两个性质:
1. 子集里所有数全为 0 全为1
2. 子集里任意两个数都在同一行或者同一列。
求一共有多少满足的子集。
思路&&题解:由性质 2 可以很快得到,子集中的所有元素必须都在同一行或同一列。于是可以用两个bitset分别存行和列的元素值,这样用 bitset count 函数可以很快得到 1 的个数,0的个数的话只要减一下就行了。之后就是跟组合数有关的了,对于组合数我们可以预处理,而每行的答案就是 cnt0i=1(cnt0i)+cnt1i=1(cnt1i) 了,然后计算一下就行了。

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int mp[55][55];
ll C[55][55];
bitset<55> b[55],c[55];
ll sum=0;
int n,m;
void init() {
    for(int i=0;i<=50;i++)
        C[i][0]=C[i][i]=1;
    for(int i=1;i<=50;i++) {
        for(int j=1;j<i;j++) {
            C[i][j]=C[i-1][j-1]+C[i-1][j];
        }
    }
}
int main() {
    init();
    cin>>n>>m;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m;j++) {
            cin>>mp[i][j];
            b[i][j]=mp[i][j];
            c[j][i]=mp[i][j];
        }
    }
    sum=(ll)n*m;
    for(int i=1;i<=n;i++) {
        int tmp=b[i].count();
        ll tot=0;
        for(int j=2;j<=tmp;j++)
            tot+=C[tmp][j];
        for(int j=2;j<=m-tmp;j++)
            tot+=C[m-tmp][j];
        sum+=tot;
    }
    for(int i=1;i<=m;i++) {
        int tmp=c[i].count();
        ll tot=0;
        for(int j=2;j<=tmp;j++)
            tot+=C[tmp][j];
        for(int j=2;j<=n-tmp;j++)
            tot+=C[n-tmp][j];
        sum+=tot;
    }
    cout<<sum<<endl;
}

C. Sorting by Subsequences

题意:给你一个长度为 n 的无序数组,让你将它分为几个子序列,将每个序列排序一遍(不改变他们在原数组中的位置),使该数组重新有序。求最多可分成几个子序列。
思路&&题解:其实这题就是让你找原数组与排序后的数组中位置上的循环有几个(差不多这意思吧讲不清qwq)…

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn=100050;
int n;
struct node {
    int x,id;
}a[maxn];
struct ans {
    int num;
    vector<int> arr;
}ask[maxn];
int tot=0;
int cnt=0,pos=1;
bool vis[maxn];
bool cmp(node a,node b) {
    return a.x<b.x;
}
int main() {
    cin>>n;
    for(int i=1;i<=n;i++) {
        cin>>a[i].x;
        a[i].id=i;
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++) {
        if(!vis[i]) {
            pos=i;
            tot++;
            while(!vis[pos]) {
                ask[tot].arr.push_back(a[pos].id);
                ask[tot].num++;
                cnt++;
                vis[pos]=1;
                pos=a[pos].id;
            }
        }
    }
    cout<<tot<<endl;
    for(int i=1;i<=tot;i++) {
        cout<<ask[i].num<<" ";
        sort(ask[i].arr.begin(),ask[i].arr.end());
        for(int j=0;j<ask[i].num;j++)
            cout<<ask[i].arr[j]<<" ";
        cout<<'\n';
    }
    return 0;
}

比赛时候我找循环时是每次从1开始找第一个 vis[i]=0 的,所以会 TLE

D. Interactive LowerBound

P.S.这是一道交互题(我之前从来没做过啊喂…qwq)

题意:给你一个已经排好序的链表,给定链表长度为 n ,最小的数在第st个位置,给定一个 k ,让你在小于等于1999次询问内确定比 k 大的最小的数。规定每次输出有两种可能:
1:? x,表示询问第 x 位的数值和比第一个他大的元素的位置。
2:! x,表示确定题目中符合给定的数值为 x 并退出程序。
每次输出后均要flush一遍。
思路&&题解:先随机 1000 次,第一次固定询问最小的那个元素,即? st。然后用随机种子 rand 出999次,并询问该位置。如果最小的元素数值大于等于 k ,那么直接输出该数值就行了。之后从这1000次询问中挑出小于k的最大值往上暴力找到第一个大于等于 k <script type="math/tex" id="MathJax-Element-3425">k</script>的就行了。

代码如下:

#include<bits/stdc++.h>
#define F fflush(stdout)
using namespace std;
const int maxn=50050;
typedef pair<int,int> pii;
pii now;
int val[maxn],nxt[maxn],pos[maxn];
int n,st,k;
inline pii que(const int& x) {
    printf("? %d\n",x),F;
    pii now;
    scanf("%d%d",&now.first,&now.second);
    return now;
}
inline void pri(const int& x) {
    printf("! %d\n",x),F;
    exit(0);
}
int main() {
    srand(unsigned((long long)new char));
    scanf("%d%d%d",&n,&st,&k);
    now=que(st);
    pos[1]=st;
    val[1]=now.first;
    nxt[1]=now.second;
    if(now.first>=k)
        pri(now.first);
    for(int i=2;i<=1000;i++) {
        int tmp=(rand()*(rand()+rand())%n*rand())%n+1;
        pos[i]=tmp;
        now=que(tmp);
        val[i]=now.first;
        nxt[i]=now.second;
    }
    int maxindex=1;
    for(int i=2;i<=1000;i++)
        if(val[i]>val[maxindex]&&val[i]<k)
            maxindex=i;
    int nowpos=pos[maxindex],nextpos=nxt[maxindex],value=val[maxindex];
    while(value<k) {
        if(nextpos==-1)
            pri(-1);
        nowpos=nextpos;
        now=que(nowpos);
        value=now.first;
        nextpos=now.second;
    }
    pri(value);
    return 0;
}

E. Upgrading Tree

暂时还没写..先放一放..

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值