题目链接

https://codeforces.com/problemset/problem/1399/D

解题思路

网上题解全部都是用的栈,我没想到用栈。
我的本质思路:
用cnt表示序列序号(在遍历过程种可能为负)
顺序遍历字符串,遇到1,我们先让cnt加一再对此时1的所在序列序号赋值;遇到0,我们先用cnt对0所在的序列序号赋值,再让cnt加一,最终所有字符得到自己所在序列的序号,但是存在序列序号为负数的情况,因此我们需要对它们的序号进行修改,修改至最小的序号为1,输出就行了。
举个例子:
1 0 0 0 1 1
初始化cnt为0,遍历字符串,第一个字符为1,此时,++cnt,并给1所在的序列的序号赋值为cnt,即1:
图片说明
第二个字符为0,此时,先对0所在的序列的序号赋值为cnt,即1,再对cnt--:
图片说明
第三个字符为0,此时,先对0所在的序列的序号赋值为cnt,即0,再对cnt--:
图片说明
第四个字符为0,此时,先对0所在的序列的序号赋值为cnt,即-1,再对cnt--:
图片说明
第五个字符为1,此时,先++cnt,再对1所在的序列的序号赋值为cnt,即-1:
图片说明
第六个字符为1,此时,先++cnt,再对1所在的序列的序号赋值为cnt,即0:
图片说明

现在我们得到从左到右每个字符所在序列序号为1 1 0 -1 -1 0,很显然,我们需要将序号调整到1 ~ n区间(不一定必须达到n)。我们只要找到最小的序号,让所有序号加上最小的那个的序号的绝对值再加一就可以将最小序号调整为1了,其他的也加上了相应的值,得到最终答案。

稍微说明一下我的思路的本质:
不同的两个字符之间若存在相同个数的1与0,那么就将这两个字符所在序列序号设为相同的。
还是上面那个例子,索引3对应的0与索引6对应的1之间具有1个0,一个1,此时索引3对应的0与索引6对应的1所在序列为同一序列,序号同为0,其他一样。

至于证明,我真不大会……

AC代码

//遇到1就先++再赋值,遇到0就先赋值再--
//最后把负值调整为正 
#include<bits/stdc++.h>
#define ll long long

using namespace std;

const int N=3e5+100;
int T,mn,ans[N],cnt,n,tmp,mx;
string str;

int main()
{
    cin>>T;
    while(T--)
    {
        memset(ans,0,sizeof ans);
        mx=0;
        mn=N+10;
        cnt=0;

        cin>>n;
        cin>>str;
        str='.'+str;

        for(int i=1;i<=n;i++)
        {
            if(str[i]=='0') mn=min(cnt,mn),ans[i]=cnt--;//确定所在序列序号同时记录最小序列序号 
            else ans[i]=++cnt,mx=max(mx,cnt);
        }

        if(mn<=0)//如果存在某个字符所在序列序号为非正 
        {
            for(int i=1;i<=n;i++)
            ans[i]+=-mn+1;//调整为正的
            mx+=-mn+1;
        }
        cout<<mx<<endl;
        for(int i=1;i<n;i++) 
        cout<<ans[i]<<' ';
        cout<<ans[n]<<endl;
    } 
}

总结

灵感的爆发吧……虽然只爆发出个1200分的题……
咋说呢,因为这几天心态有点爆炸,做出这个题对我的鼓励还是挺大的。
这个代码我debug了3小时,因为我觉得它必对老子就不信了,中午吃了个饭睡了个觉,下午一来就发现自己犯了个shabi错误,我他妈居然没有先输出序列总数!!!我他妈眼瞎了,样例复制了无数次,居然都没发现少输出东西了!!!我他妈裂开!!!
总而言之做出来了,老子不是很菜!!!……应该吧……