ICPC2021沈阳 部分题解

E. Edward Gaming, the Champion(签到题)

题意: 求一个字符串s中有多少个"edgnb"

Solution: EDGNBEDGNBEDGNB

#include

using namespace std;

/**/

const int N = 2e5 + 5;

char s[N];

/**/

int main()

{

scanf("%s", s);

int len = strlen(s), ans = 0;

for(int i = 0; i < len; i++)

{

if(i+4>=len) break;

if(s[i]=='e'&&s[i+1]=='d'&&s[i+2]=='g'&&s[i+3]=='n'&&s[i+4]=='b') ans++, i = i + 4;

}

cout<

return 0;

}

F. Encoded Strings I(签到题/模拟)

题意: 队友A的,待补。

Solution: 队友A的,待补。

#include

#define il inline

#define ll long long

#define Max 1005

#define int ll

using namespace std;

il ll read()

{

char c=getchar();

ll x=0,f=1;

while(c>'9'||c<'0')

{

if(c=='-') f=-1;

c=getchar();

}

while(c>='0'&&c<='9')

{

x=x*10+c-'0';

c=getchar();

}

return x*f;

}

int n,a[Max],c[Max],id[Max];

char s[Max],t[Max][Max];

il bool cmp(int x,int y)

{

int l=min(strlen(t[x]+1),strlen(t[y]+1));

for(int i=1;i<=l;i++)

{

if(t[x][i]

if(t[x][i]>t[y][i]) return 1;

}

return strlen(t[x]+1)>strlen(t[y]+1);

}

signed main()

{

n=read();

scanf("%s",s+1);

for(int i=1;i<=n;i++)

{

for(int j=1;j<=26;j++) c[j]=0;

int tot=0;

for(int j=i;j>=1;j--)

{

if(!c[s[j]-'a'+1]) t[i][j]=tot+'a',c[s[j]-'a'+1]=++tot;

else t[i][j]=c[s[j]-'a'+1]+'a'-1;

//cout<

}

}

//for(int i=1;i<=n;i++) puts(t[i]+1);

for(int i=1;i<=n;i++) id[i]=i;

sort(id+1,id+1+n,cmp);

puts(t[id[1]]+1);

}

J. Luggage Lock(bfs)

题意: 一个四位数密码。给定起始状态 A 和终止状态 B 。每次可以选择一段区间向上拧或向下拧(即+1/−1+1/-1+1/−1)。

求由A变为B的最小操作次数。

Solution: 因为所有的两组状态都可以转化成 起始状态为 0000。因此对于 0000 开始,其实只有 10000 种状态。

预处理:利用 bfs 求最少步数。同时对已经得到的状态进行标记即可。

然后即可对每组询问 O(1) 输出。

#include

#define PII pair

using namespace std;

inline void read(int &a) {a=0;int c=getchar(),b=1; while(c>'9'||c<'0') {if(c=='-')b=-1;c=getchar();} while(c>='0'&&c<='9') a=(a<<3)+(a<<1)+c-48,c=getchar();a*=b; }

/**/

const string opt[15] = {"0001","0011","0111","1111","0010","0110","1110","0100","1100","1000"};

int vis[10005], ans[10005];

/**/

inline int get(string s)

{

int ret = 0;

for(int i=0;i<4;i++) ret=(ret<<3)+(ret<<1)+s[i]-'0';

return ret;

}

inline string work(string s,int p,int flag)

{

string ret = s;

for(int i=0;i<4;i++)

{

if(opt[p][i]=='0') continue;

ret[i]=(ret[i]-'0'+flag+10)%10+'0';

}

return ret;

}

int main()

{

queueq;

q.push(PII("0000",0));

vis[0] = 1;

while(!q.empty())

{

PII t = q.front(); q.pop();

string u = t.first;

int step = t.second;

for (int i = 0; i < 10; i++)

{

string s1 = work(u,i,1);

string s2 = work(u,i,-1);

int num1 = get(s1), num2 = get(s2);

if(!vis[num1])

{

vis[num1]=1;

ans[num1]=step + 1;

q.push(PII(s1, step + 1));

}

if(!vis[num2]) vis[num2] = 1, ans[num2] = step + 1, q.push(PII(s2, step + 1));

}

}

int t;

read(t);

string S1, S2, T;

while(t--)

{

cin >> S1 >> S2;

int ret = 0;

for(int i=0;i<4;i++)

{

ret = ret * 10 + (S2[i]-S1[i]+10)%10;

}

cout << ans[ret] << '\n';

}

return 0;

}

B. Bitwise Exclusive-OR Sequence(铜牌题)

题意: 一段长度为 nnn 的数。给定 mmm 组关系。每组关系 (u,v,w)(u,v,w)(u,v,w) 描述了:au⊕av=wa_u \oplus a_v=wau​⊕av​=w

求这组数 各元素和 的最小值。

Solution: 可以根据每组关系建边,随意以一个有出边的点为树根进行遍历。

定义 dis[u]dis[u]dis[u] 为 从根节点到 uuu 的边权异或和。根据异或性质易知:art⊕au=disua_{rt} \oplus a_u = dis_uart​⊕au​=disu​

如果出现两个不同值的 dis[u]dis[u]dis[u] 则数据出现矛盾输出 -1。

对该森林的其中一个树,tot[i]tot[i]tot[i] 表示所有 dis[u]dis[u]dis[u] 的第 iii 位为1的数量。超过一半则让根该位为1更优,否则让根该位为0。

#include

#define ll long long

using namespace std;

inline void read(int &a) {a=0;int c=getchar(),b=1; while(c>'9'||c<'0') {if(c=='-')b=-1;c=getchar();} while(c>='0'&&c<='9') a=(a<<3)+(a<<1)+c-48,c=getchar();a*=b; }

/**/

const int N = 1e5 + 5, M = 4e5 + 5;

int n, m, head[N], cnt, dis[N], tot[N], vis[N], sum;

bool err;

ll ans;

struct edge

{

int nxt, to, w;

}e[M];

/**/

inline void add(int u, int v, int w)

{

e[++cnt] = (edge){head[u], v, w};

head[u] = cnt;

}

void dfs(int u, int fa, int ti)

{

if(err) return;

for(int i = head[u]; i; i = e[i].nxt)

{

int v = e[i].to;

if(v == fa) continue;

if(vis[v])

{

if((dis[u] ^ e[i].w) == dis[v]) continue;

else {err = 1; return;}

}

vis[v] = ti;

dis[v] = (dis[u] ^ e[i].w);

sum++;

for(int j = 0; j <= 30; j++)

{

if(dis[v] & (1<

}

dfs(v, u, ti);

if(err) return;

}

}

int main()

{

read(n); read(m);

for(int i = 1, u, v, w; i <= m; i++)

{

read(u); read(v); read(w);

if(u==v)

{

if(w) err = 1;

continue;

}

add(u, v, w);

add(v, u, w);

}

if(err) {puts("-1");return 0;}

for(int i = 1; i <= n; i++)

{

if(!head[i]) continue;

if(vis[i]) continue;

vis[i] = i;

dfs(i, 0, i); sum++;

for(int j = 0; j <= 30; j++)

{

ans += 1ll * min(tot[j], sum - tot[j]) * (1 << j);

tot[j] = 0;

}

sum = 0;

if(err) {puts("-1"); return 0;}

}

if(err) {puts("-1"); return 0;}

cout << ans << '\n';

return 0;

}

L. Perfect Matchings(树形dp+容斥)

题意: 队友A的,待补。

Solution: 队友A的,待补。

#include

#define il inline

#define ll long long

#define Max 4010

#define Mod 998244353

#define int ll

using namespace std;

il ll read()

{

char c=getchar();

ll x=0,f=1;

while(c>'9'||c<'0')

{

if(c=='-') f=-1;

c=getchar();

}

while(c>='0'&&c<='9')

{

x=x*10+c-'0';

c=getchar();

}

return x*f;

}

struct node

{

int t,nt;

}e[Max<<1];

int n,head[Max],tot,f[Max][Max][2],g[Max],sz[Max],ans;

il void add(int u,int v)

{

e[++tot].t=v;

e[tot].nt=head[u];

head[u]=tot;

}

il void dfs(int u,int fa)

{

sz[u]=1;

for(int i=head[u];i;i=e[i].nt)

{

int v=e[i].t;

if(v==fa) continue;

dfs(v,u);

sz[u]+=sz[v];

}

int s=1;

//f[u][0][0]=1;

f[u][0][0]=1;

for(int i=head[u];i;i=e[i].nt)

{

int v=e[i].t;

if(v==fa) continue;

for(int j=s/2+1;j>=0;j--)

{

for(int k=1;k<=sz[v]/2+1;k++)

{

//if(j==0&&k==0) continue;

f[u][j+k][0]=(f[u][j+k][0]+f[u][j][0]*f[v][k][0]+f[u][j][0]*f[v][k][1])%Mod;

//f[u][0][0]=1;

f[u][j+k][1]=(f[u][j+k][1]+f[u][j][0]*f[v][k-1][0]+f[u][j][1]*(f[v][k][0]+f[v][k][1]))%Mod;

//cout<

}

}

s+=sz[v];

}

}

signed main()

{

n=read()*2;

for(int i=1;i

{

int u=read(),v=read();

add(u,v),add(v,u);

}

g[0]=1;

for(int i=2;i<=n;i+=2) g[i]=(i-1)*g[i-2]%Mod;

dfs(1,0);

//for(int i=0;i<=n/2;i++) cout<

for(int i=0;i<=n/2;i++)

{

ans=ans+(g[n-i*2]*(f[1][i][1]+f[1][i][0])%Mod*(i&1?-1:1)+Mod);

ans=(ans+Mod)%Mod;

}

cout<

}

M. String Problem(KMP)

题意: 给定一个长度为 nnn 的字符串s。输出 nnn 行 (li,ri)(l_i, r_i)(li​,ri​) 表示:

对 s 的前缀 s1...is_{1...i}s1...i​ 中字典序最大的子串。

Solution: 首先容易得到 ri=ir_i = iri​=i。然后做了一个神奇的 KMP 。

#include

using namespace std;

const int N = 1e6 + 5;

char s[N];

int nxt[N];

int main()

{

scanf("%s", s + 1);

int len = strlen(s + 1);

puts("1 1");

int pos = 1, j = 1;

for(int i = 2; i <= len; i++)

{

while(j && s[i] > s[pos + nxt[j]])

{

pos += (j - nxt[j]);

j = nxt[j];

}

if(j && s[i] == s[pos + nxt[j]])

{

j++;

nxt[j] = nxt[j - 1] + 1;

}

else nxt[++j] = 0;

printf("%d %d\n", pos, i);

}

return 0;

}