Codeforces Round #302 解题报告

时间:2021-06-21 23:57:16

Codeforces Round #302 解题报告

  感觉今天早上虽然没有睡醒但是效率还是挺高的...

  Pas和C++换着写...


  

544A. Set of Strings
 

You are given a string q. A sequence of k strings s1, s2, ..., sk is called beautiful, if the concatenation of these strings is string q(formally, s1 + s2 + ... + sk = q) and the first characters of these strings are distinct.

Find any beautiful sequence of strings or determine that the beautiful sequence doesn't exist.

  每个在之前没有出现过的字母作一个标记,作为每个字符串的开头

  如果数量不足则输出-1

 var i,k,j:longint;
vis:array['a'..'z']of boolean;
v:array[-..]of boolean;
s:ansistring; begin
readln(k);
readln(s);
fillchar(vis,sizeof(vis),true);
fillchar(v,sizeof(v),true);
for i:= to length(s) do if vis[s[i]] then
begin
vis[s[i]]:=false;v[i]:=false;dec(k);
if k= then break;
end;
if k> then writeln('NO') else
begin
writeln('YES');
i:=;
while i<=length(s) do
begin
write(s[i]);
j:=i+;
while (j<=length(s))and(v[j]) do
begin
write(s[j]);
inc(j);
end;
writeln;
i:=j;
end;
end;
end.

544B. Sea and Islands
 

A map of some object is a rectangular field consisting of n rows and n columns. Each cell is initially occupied by the sea but you can cover some some cells of the map with sand so that exactly k islands appear on the map. We will call a set of sand cells to be island if it is possible to get from each of them to each of them by moving only through sand cells and by moving from a cell only to a side-adjacent cell. The cells are called to be side-adjacent if they share a vertical or horizontal side. It is easy to see that islands do not share cells (otherwise they together form a bigger island).

Find a way to cover some cells with sand so that exactly k islands appear on the n × n map, or determine that no such way exists.

  又是一道看样例就会思路乱掉的题..

  非常简单,每个小岛占一格就好了..

  直接按照横纵坐标和差的奇偶性判断是否放沙

 var i,j,n,k:longint;
begin
readln(n,k);
if k>(n*n+) >> then writeln('NO') else
begin
writeln('YES');
for i:= to n do
begin
for j:= to n do if ((i+j) and =)and(k>) then
begin
write('L');dec(k);
end else write('S');
writeln;
end;
end;
end.

543A. Writing Code

Programmers working on a large project have just received a task to write exactly m lines of code. There are n programmers working on a project, the i-th of them makes exactly ai bugs in every line of code that he writes.

Let's call a sequence of non-negative integers v1, v2, ..., vn a plan, if v1 + v2 + ... + vn = m. The programmers follow the plan like that: in the beginning the first programmer writes the first v1 lines of the given task, then the second programmer writes v2 more lines of the given task, and so on. In the end, the last programmer writes the remaining lines of the code. Let's call a plan good, if all the written lines of the task contain at most b bugs in total.

Your task is to determine how many distinct good plans are there. As the number of plans can be large, print the remainder of this number modulo given positive integer mod.

  感觉比赛的时候脑子坏掉了...

  一直觉得要枚举一个数量所以n^4...甚至想要去写二进制背包这种两年没写过的东西...

  然而不确定数量的背包直接正序就好了啊...

 #include<cstdio>
#include<cstdlib>
#include<cstring>
int a[],f[][];
int main(){
int n,m,b,tt;
scanf("%d%d%d%d",&n,&m,&b,&tt);
for (int i=;i<=n;i++) scanf("%d",&a[i]);
f[][] = ;
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
for (int k=a[i];k<=b;k++) f[j][k]=(f[j][k]+f[j-][k-a[i]])%tt;
int ans=;
for (int i=;i<=b;i++) ans=(ans+f[m][i])%tt;
printf("%d\n",ans);
return ;
}

543B. Destroying Roads
 

In some country there are exactly n cities and m bidirectional roads connecting the cities. Cities are numbered with integers from 1 to n. If cities a and b are connected by a road, then in an hour you can go along this road either from city a to city b, or from city b to city a. The road network is such that from any city you can get to any other one by moving along the roads.

You want to destroy the largest possible number of roads in the country so that the remaining roads would allow you to get from city s1to city t1 in at most l1 hours and get from city s2 to city t2 in at most l2 hours.

Determine what maximum number of roads you need to destroy in order to meet the condition of your plan. If it is impossible to reach the desired result, print -1.

  好多叉点的题...

  首先为了使总路程最短...肯定首先想到分别求最短路...

  然而还有一种方法,就是让两条路径重合,可以证明得到,重合的部分一定是连续的一段

  因为如果有多段的话,中间分开的部分合在一起一定是一个更优的解

  所以可以用n次bfs求出多源最短路

  将上面几种方法尝试一下然后更新出答案

  然而还要注意两个点分别连接的不一定都是起点或都是终点,要讨论一下...

 const maxn = ;maxm=;INF=;
var fa,next:array[-..maxm]of longint;
link,opt:array[-..maxn]of longint;
vis:array[-..maxn]of boolean;
dis:array[-..maxn,-..maxn]of longint;
ans,s1,t1,h1,s2,t2,h2,n,m,e,i,x,y,j:longint; procedure add(x,y:longint);
begin
inc(e);fa[e]:=y;next[e]:=link[x];link[x]:=e;
inc(e);fa[e]:=x;next[e]:=link[y];link[y]:=e;
end; function min(a,b:longint):longint;
begin
if a<b then exit(a) else exit(b);
end; function max(a,b:longint):longint;
begin
if a>b then exit(a) else exit(b);
end; procedure bfs(s:longint);
var head,tail,x,j,i:longint;
begin
for i:= to n do dis[i,s]:=INF;
fillchar(vis,sizeof(vis),true);
head:=;tail:=;dis[s,s]:=;vis[s]:=false;opt[]:=s;
while head<>tail do
begin
inc(head);
x:=opt[head];j:=link[x];
while j<> do
begin
if vis[fa[j]] then
begin
vis[fa[j]]:=false;
dis[fa[j],s]:=dis[x,s]+;
inc(tail);opt[tail]:=fa[j];
end;
j:=next[j];
end;
end;
end; begin
readln(n,m);
e:=;
for i:= to m do
begin
readln(x,y);add(x,y);
end;
for i:= to n do bfs(i);
readln(s1,t1,h1);readln(s2,t2,h2);
ans:=-;
if (dis[s1,t1]<=h1)and(dis[s2,t2]<=h2) then ans:=max(ans,max(,m-dis[s1,t1]-dis[s2,t2]));
for i:= to n do
for j:= to n do
begin
if (dis[i,s1]+dis[i,j]+dis[j,t1]<=h1)and(dis[i,s2]+dis[i,j]+dis[j,t2]<=h2) then
ans:=max(ans,m-dis[i,s1]-dis[i,s2]-dis[i,j]-dis[j,t1]-dis[j,t2]);
if (dis[i,s1]+dis[i,j]+dis[j,t1]<=h1)and(dis[i,t2]+dis[i,j]+dis[j,s2]<=h2) then
ans:=max(ans,m-dis[i,s1]-dis[i,t2]-dis[i,j]-dis[j,s2]-dis[j,t1]);
end;
for i:= to n do
for j:= to n do if (dis[i,s1]+dis[i,t1]<=h1)and(dis[j,t2]+dis[j,s2]<=h2) then
ans:=max(ans,max(,m-dis[i,s1]-dis[i,t1]-dis[j,s2]-dis[j,t2]));
if (h1>=dis[s1,t1])and(h2>=dis[s2,t2]) then ans:=max(ans,m-h1-h2);
writeln(ans);
end.

543C. Remembering Strings

You have multiset of n strings of the same length, consisting of lowercase English letters. We will say that those strings are easy to remember if for each string there is some position i and some letter c of the English alphabet, such that this string is the only string in the multiset that has letter c in position i.

For example, a multiset of strings {"abc", "aba", "adc", "ada"} are not easy to remember. And multiset {"abc", "ada", "ssa"} is easy to remember because:

  • the first string is the only string that has character c in position 3;
  • the second string is the only string that has character d in position 2;
  • the third string is the only string that has character s in position 2.

You want to change your multiset a little so that it is easy to remember. For aij coins, you can change character in the j-th position of thei-th string into any other lowercase letter of the English alphabet. Find what is the minimum sum you should pay in order to make the multiset of strings easy to remember.

  首先考试的时候想到状压DP但是只会平方的乱搞...

  况且看起来D更简单所以果断弃C..(事实证明这个想法太Naive

  实际上也非常好想...对于一个给定的状态用二进制表示

  我们可以从任意一位未满足题意的字符串操作,然而又要保证所有的为满足题意的字符串都被操作过

  因为显然和操作的顺序没有关系,因此没必要每次枚举操作哪位

  不妨每次都操作第一个不满足题意的位置

  很显然:如果当前位置是这一列中独一无二的字符,直接向下面的状态转移当前所得的最优解

  如果这一位不是,我们可以修改这一位,然后转移的时候加上花费

  但是有一种优秀的处理方法:就是假设一列中某个字符出现了x次,如果其余x-1个字符都修改掉了,剩下一个也就独一无二了

  然后我们再处理这种情况

  可以预处理出这一列中字符相同的位置、个数、总花费和最大花费

  显然,向下转移的状态是把这些位置都更新成满足题意,花费为总花费-最大花费

  这些都在预处理中实现,DP的时候就可以做到O(1)

  另外DP的顺序不能按照大小而是1的个数从小到大~

  [UPD.05.13]DP的顺序实际上可以直接从小到大...因为不管哪一位加了多少个1肯定比当前数大..

 #include<cstdio>
#include<cstdlib>
#include<cstring>
const int maxn=,INF=,maxm=;
struct node{
int tot,sum,mx,cov;
}a[maxn][maxn];
int n,m,f[maxm],len[maxm],q[maxm],tot[],sum[],mx[],cov[],map[maxn][maxn],p[maxn][maxn];
char s[];
int calc(int x){
int ans=;
for (;x;x=x>>) ans+=x&;
return ans;
} int max(int a,int b){
if (a>b) return a;
return b;
} int min(int a,int b){
if (a<b) return a;
return b;
} int getf(int x){
for (int i=;i<=n;i++){
if ((x&)==) return (n-i+);
x=x >> ;
}
}
void sortf(int l,int r){
int i=l,j=r,mid=len[(l+r)>>];
do{
while (i<r&&len[i]<mid) i++;
while (l<j&&len[j]>mid) j--;
if (i<=j){
len[]=len[i];len[i]=len[j];len[j]=len[];
q[]=q[i];q[i]=q[j];q[j]=q[];
i++;j--;
}
}while (i<=j);
if (i<r) sortf(i,r);
if (l<j) sortf(l,j);
} int main(){
scanf("%d%d",&n,&m);gets(s);
char ch;
for (int i=;i<=n;i++){
for (int j=;j<=m;j++){
scanf("%c",&ch);
map[i][j]=ch;
}
gets(s);
}
for (int i=;i<=n;i++)
for (int j=;j<=m;j++) scanf("%d",&p[i][j]);
for (int i=;i<=m;i++){
memset(cov,,sizeof(cov));
memset(sum,,sizeof(sum));
memset(mx,,sizeof(mx));
memset(tot,,sizeof(tot));
for (int j=;j<=n;j++){
int ch=map[j][i];
cov[ch]+= << (n-j);
sum[ch]+=p[j][i];
mx[ch]=max(mx[ch],p[j][i]);
tot[ch]++;
}
for (int j=;j<=n;j++){
int ch=map[j][i];
a[j][i].cov=cov[ch];
a[j][i].sum=sum[ch];
a[j][i].mx=mx[ch];
a[j][i].tot=tot[ch];
}
}
for (int i=;i<=(<<n)-;i++) q[i+]=i,len[i+]=calc(q[i+]);
sortf(,(<<n)-);
memset(f,INF,sizeof(f));
f[]=;
for (int i=;i<=(<<n)-;i++)if (f[q[i]]!=f[maxm-]){
int x=getf(q[i]);
for (int j=;j<=m;j++){
if (a[x][j].tot==) f[q[i]|(<<(n-x))]=min(f[q[i]|(<<(n-x))],f[q[i]]);
else{
f[q[i]|(<<(n-x))]=min(f[q[i]|(<<(n-x))],f[q[i]]+p[x][j]);
f[q[i]|a[x][j].cov]=min(f[q[i]|a[x][j].cov],f[q[i]]+a[x][j].sum-a[x][j].mx);
}
}
}
printf("%d\n",f[(<<n)-]);
return ;
}

543D. Road Improvement
 

The country has n cities and n - 1 bidirectional roads, it is possible to get from every city to any other one if you move only along the roads. The cities are numbered with integers from 1 to n inclusive.

All the roads are initially bad, but the government wants to improve the state of some roads. We will assume that the citizens are happy about road improvement if the path from the capital located in city x to any other city contains at most one bad road.

Your task is — for every possible x determine the number of ways of improving the quality of some roads in order to meet the citizens' condition. As those values can be rather large, you need to print each value modulo 1 000 000 007 (109 + 7).

  这道题订正了很久啊...

  其实还是非常容易想到做法的...

  对于每个叶子值赋为1(含义大概就是这样的道路为0的方案)

  然后对于一个节点,答案为所有(子树的答案+1)的乘积

  就这棵子树而言,除了下面的答案外,将连接子树与父亲节点的边毁掉也是一种方案,因此+1

  另外,子树与子树之前答案互不影响,所以可以相乘

  刚开始问自己:不是应该枚举一下有几棵子树要毁掉,分别毁掉哪几棵子树吗?那样复杂度就呵呵了啊...

  但是忘记了累计答案的时候,将道路为0也就是不毁掉任何边的方案累计了一个1

  他们的乘积恰好包括了所有的情况

  然后转换到n个节点..经典的两次dfs计算答案...

  但是还是WA了

  后来进行的一切努力...都是在拯救取模导致答案错误的问题

  首先一个简单的反例:

  如果有一棵子树的答案是模数-1,然后我们将子树+1连乘的时候答案就是0

  对于这个答案而言并没有错

  但是在我们第二次dfs计算上面部分的答案的时候,要将当前子树对父亲节点的贡献除掉

  这个时候就没有办法得到父亲节点在乘上模数之前的答案了

  

  改进的方法:

  增加一个标记vis,表示在向下的答案中出现的因子为模数的个数

  一旦发现儿子节点的答案+1=模数,那么这个标记+1,答案不动

  实际上还是错了..

    上面的操作是建立在儿子节点答案的vis=0的情况..

    因为如果儿子节点的答案中有模数这个因子了,那么它对父亲的贡献=1,相当于不变

  

  在第二次dfs的过程中

  如果当前节点的vis>0也就是有模数这个因子的话,我们是没有统计它对父亲的答案的

    如果父亲的vis=0,那么就直接统计答案,不需要除去当前节点的贡献

    如果父亲的vis>0,那么父亲的答案中有模数这个因子,然后就直接=1

  当前节点的vis=0

    如果当前节点的向下答案+1=模数的话

      如果父亲节点的vis=1也就是只有当前节点这一个模数因子

        那么此时父亲节点的答案正好是除去当前节点贡献的答案,统计即可

      否则,也就是父亲节点中除了当前节点的贡献之外还有别的模数因子,此时答案为1

    当前节点向下答案+1<>模数,此时是最正常的情况了...

    然而也要讨论父亲节点的vis标记

      如果它=0,那就直接统计答案,否则,答案=1

 const maxn = ;ttt = ;
var n,e,i,j,x:longint;
fa,next,link,vis:array[-..maxn]of longint;
a:array[-..maxn]of record sum,sum2,x1,x2:int64;end;
tt:int64; procedure add(x,y:longint);
begin
inc(e);fa[e]:=y;next[e]:=link[x];link[x]:=e;
inc(e);fa[e]:=x;next[e]:=link[y];link[y]:=e;
end; procedure ex_Euclid(a,b:int64;var x,y:int64);
var t:int64;
begin
if b= then
begin
x:=;y:=;
exit;
end else
begin
ex_Euclid(b,a mod b,x,y);
t:=x;x:=y;y:=t-(a div b)*y;
end;
end; function inverse(a:int64):int64;
var x,y:int64;
begin
ex_Euclid(a,tt,x,y);
while x< do inc(x,tt);
exit(x);
end; procedure dfs1(p:longint);
var j:longint;
begin
a[p].sum:=;vis[p]:=;
j:=link[p];
while j<> do
begin
if vis[fa[j]]= then
begin
dfs1(fa[j]);
if a[fa[j]].x1= then
begin
if a[fa[j]].sum+=tt then inc(a[p].x1) else
a[p].sum:=(a[p].sum*(a[fa[j]].sum+))mod tt;
end;
end;
j:=next[j];
end;
end; procedure dfs2(fat,p:longint);
var j:longint;
begin
vis[p]:=;
if fat<> then
begin
if a[p].x1<> then
begin
if a[fat].x1= then a[p].sum2:=(a[fat].sum2*a[fat].sum+) mod tt
else a[p].sum2:=;
end else
begin
if a[p].sum+=tt then
begin
if a[fat].x1> then a[p].sum2:= else
a[p].sum2:=(a[fat].sum2*a[fat].sum+) mod tt;
end else
begin
if a[fat].x1> then a[p].sum2:= else
a[p].sum2:=(a[fat].sum2*a[fat].sum mod tt*inverse(a[p].sum+)+) mod tt;
end;
end;
end else a[p].sum2:=;
j:=link[p];
while j<> do
begin
if vis[fa[j]]= then
dfs2(p,fa[j]);
j:=next[j];
end;
end; begin
tt:=;
readln(n);tt:=tt;
e:=;
for i:= to n do
begin
read(x);add(i,x);
end;
fillchar(vis,sizeof(vis),);
dfs1();dfs2(,);
tt:=tt;
for i:= to n do if a[i].x1> then a[i].sum:=;
for i:= to n- do write((a[i].sum*a[i].sum2) mod tt,' ');
writeln(a[n].sum*a[n].sum2 mod tt);
end.

  

  还有十天就要二试了呢...完全没有反应过来啊...

  08/.May