[2021ICPC沈阳补题]更新至4题
碎碎念
嘿嘿嘿明天就沈阳了今天考完计组心情大好开始重拾烂摊子补题
E. Edward Gaming, the Champion
题意
给出一个长度不超过2e5的由小写字母组成的字符串,求出字符串中"edgnb"出现的个数
解析
直接统计即可,这里粘了队友的代码,是我的话应该会另一种写法
#include <bits/stdc++.h>
using namespace std;
string str;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin >> str;
string ddd = "edgnb";
int cnt = 0;int ptr = 0;
for(int i = 0;i < str.length();i ++)
{
if(str[i] != ddd[ptr])
{
ptr = 0;
}
if(str[i] == ddd[ptr])
{
ptr ++;
}
if(ptr == 5)
{
cnt ++;
}
}
cout << cnt;
//edgnbeeedgnbbbbedgnb
}
F. Encoded Strings I
题意
对于一个字符串s[1,2,3...n]定义一个映射字符串t[1,2,3,...n],映射字符串的字符t[i]为第(字符串长度-s[i]在s中最后一次出现的位置+1)个小写字母。
例如对于字符串abc,a最后一次出现位置为1,后面有2个字符,因此映射字符串第一位为c,依此类推。
现在给你一个字符串s,输出s的每个前缀的映射字符串中字典序最大的那个。
字符串长度<=1000
分析
\(n^2\)模拟即可。
#include <bits/stdc++.h>
using namespace std;
string str;
string ss[1010];
int occ;
int vis[360];
const int key = 'a' - 1;
signed main()
{
int n;cin >> n;
cin >> str;str =str;
for(int i = 0;i < n;i ++)
{
int occ = 1;
memset(vis,0,sizeof(vis));
ss[i].resize(i+1);
for(int j = i;j >= 0;j --)
{
char chk = str[j];
if(vis[chk - key] == 0)
{
vis[chk - key] = occ ++;
}
if(vis[chk - key] > 0)
{
char tmp = key + vis[chk - key];
ss[i][j] = tmp;
}
}
}
sort(ss,ss+n);
cout << ss[n-1] << endl;
}
B.Bitwise Exclusive-OR Sequence
题意
有n(n<=1e5)个非负整数a1,a2,...an,给定它们之间的m(m<=2e5)个关系,形如au异或av=w(0<=w<2^30),现在给出这m个关系,求最小的a数组的总和,如果条件无法满足,输出-1。
分析
vp场上很快想到二进制按位考虑,每位根据异或关系建图,在某位上如果异或=1就在这两点之间建边,二分图染色bfs跑一遍看是否会发生矛盾,如果矛盾则直接输出-1,否则取每个连通块中染色后同色数量较小的点数求和移位后加进答案中,结果一直wa3。
一个多月后补题,一眼看出这样想很明显是错的,因为没有考虑某位上两数异或=0代表两个数这一位必须相同的情况。另外是这题会卡常,建30次图的话即使复杂度正确,但1s时限,tle是必然的。
因此输入a,b,w,直接在a,b之间建权值为w的边。按位枚举建图情况时第i位取(w>>i)&1为边权即可,当前位a,b间边权为0说明a,b颜色相同,边权为1说明颜色相反,bfs或dfs跑一遍,判断是否有矛盾,如果没有再记录每个连通块中两种颜色里较小的点数,求和num,则num*(1<<i)为第i位答案的累计。
吐槽一下一个月之后重温自己的代码感觉就是两个字:x山。。。看了好久才想起来wa代码写的是啥,精简了一些地方+改掉了错误做法就过了
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=32,M=2e5+10;
int h[M*2],e[M*4],ne[M*4],w[M*4];
int st[N][M],cnt[3];
ll ans,idx;
int n,m;
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a];h[a]=idx++;
}
bool bfs(int c)
{
int num=0;
for(int j=1;j<=n;j++)
{
for(int i=1;i<=2;i++) cnt[i]=0;
if(st[c][j]!=0) continue;
if(h[j]==-1)
{
st[c][j]=3;continue;
}
queue<int> q;
q.push(j);
st[c][j]=1;cnt[1]++;
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=h[x];~i;i=ne[i])
{
int y=e[i],z=(w[i]>>c)&1;
if(st[c][y]==st[c][x]&&z==1) return 0;
if(st[c][y]!=0&&st[c][y]!=st[c][x]&&z==0) return 0;
if(st[c][y]!=0) continue;
if(st[c][x]==1)
{
if(z==1)st[c][y]=2,cnt[2]++;
else st[c][y]=1,cnt[1]++;
}
if(st[c][x]==2)
{
if(z==1)st[c][y]=1,cnt[1]++;
else st[c][y]=2,cnt[2]++;
}
q.push(y);
}
}
num+=min(cnt[1],cnt[2]);
}
ans+=(ll)num*(ll)(1<<c);
return 1;
}
main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
memset(h,-1,sizeof h);
cin>>n>>m;
while(m--)
{
int a,b,c;cin>>a>>b>>c;
add(a,b,c);add(b,a,c);
}
for(int i=0;i<=29;i++)
if(bfs(i)==0)
{
cout<<-1;
return 0;
}
cout<<ans;
}
J. Luggage Lock
我超,乃琳
题意:
行李箱密码有四位,每次操作可以将连续位的密码在模10意义下+1或者减1,例如0101经由一次操作可以变成0212,也可以变成9090等,但不能一次变成0202。现在给你行李箱的初始密码a1a2a3a4,求使它变成b1b2b3b4所需的最少操作数。
分析:
首先明确状态数其实和起始状态和终点状态都没有关系,而只和它们之间的状态差有关系,例如,1111变成9090,其实就是0000变成8989,6712变成8922,其实就是0000变成2210。以0000为起点bfs,总状态数其实只有1e5种。bfs预处理一遍0000到所有状态的最小步数,每次询问直接求出状态差输出即可。用map爆搜方便一点,复杂度nlogn可以通过。
#include<bits/stdc++.h>
using namespace std;
map<string,int> mp,mp2;
string s[10];
main()
{
s[0]="0001",s[1]="0010",s[2]="0100",s[3]="1000",s[4]="0011",s[5]="0110",s[6]="1100",s[7]="0111",s[8]="1110",s[9]="1111";
mp["0000"]=0;
queue<string> q;
q.push("0000");
while(!q.empty())
{
string x=q.front();q.pop();
mp2[x]=1;
for(int i=0;i<10;i++)
{
string x1="",x2="";
for(int j=0;j<4;j++) x1+=(s[i][j]+x[j]-2*'0')%10+'0';
for(int j=0;j<4;j++) x2+=(x[j]-s[i][j]+10)%10+'0';
if(mp2[x1]!=1) mp[x1]=mp[x]+1,mp2[x1]=1,q.push(x1);
if(mp2[x2]!=1) mp[x2]=mp[x]+1,mp2[x2]=1,q.push(x2);
}
}
int t;cin>>t;
while(t--)
{
string s1,s2;cin>>s1>>s2;
string s3="";
for(int i=0;i<4;i++)
s3+=(s2[i]-s1[i]+10)%10+'0';
cout<<mp[s3]<<'\n';
}
}

浙公网安备 33010602011771号