Problem A. String Master(master.c/cpp/pas)

题目描述

 

 所谓最长公共子串,比如串 A:“abcde”,串 B:“jcdkl”,则它们的最长公共子串为串 “cd”,即长
度最长的字符串,且在两个串中都作为连续子串出现过。
给定两个长度都为 n 的字符串,对于字符串大师的你来说,求它们的最长公共子串再简单不过了。
所以现在你有 k 次修改机会,每次你可以选择其中某个串的某个位置,将其修改成任意字符。
你需要合理使用这 k 次修改机会,使得修改之后两个串的最长公共子串最长。相信对于字符串大师
的你来说,这个问题也难不倒你。
Input
第一行包含两个整数 n, k,分别表示字符串的长度和修改次数。
第二行包含一个长度为 n 的仅由小写字符构成的字符串 S。
第三行包含一个长度为 n 的仅由小写字符构成的字符串 T。
Output
输出一行一个整数,即修改完毕之后两个串的最长公共子串的长度。

k<=n<=300

题目大意:两个字符串,任意改动k个字母,使得最长公共子串最长

思路:两个指针i,j,代表从S串第i个位置开始,T串从第j个位置开始,枚举长度len,求出在两边找出的子串在k次修改机会内的最大长度

复杂度:O(N^3)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 inline int read(){
 7     char chr=getchar();    int f=1,ans=0;
 8     while(!isdigit(chr)) {if(chr=='-') f=-1;chr=getchar();}
 9     while(isdigit(chr))  {ans=(ans<<3)+(ans<<1);ans+=chr-'0';chr=getchar();}
10     return ans*f;
11 }
12 void write(int x){
13     if(x<0) putchar('-'),x=-x;
14     if(x>9) write(x/10);
15     putchar(x%10+'0');
16 }
17 const int N=301;
18 char S[N],T[N],tS[N],tT[N];
19 int n,k,f[N][N];
20 inline int Check(char *S,char *T){
21     int ans=0;
22     for(int i=1;i<=n;i++)
23         for(int j=1;j<=n;j++){
24             int cs=0,len;
25             for(len=1;len<=min(n-i+1,n-j+1);len++){
26                 if(S[i+len-1]!=T[j+len-1]) 
27                     if(cs>=k) break;
28                     else cs++;
29             }ans=max(ans,len-1);
30         }return ans;
31     
32 }
33 int main(){
34     freopen("master.in","r",stdin);
35     freopen("master.out","w",stdout);
36     n=read(),k=read();
37     scanf("%s",S+1);scanf("%s",T+1);
38     write(Check(S,T));
39     return 0;
40 }

Problem B. Tourist Attractions(tour.c/cpp/pas)

题目描述

 

 在美丽的比特镇一共有 n 个景区,编号依次为 1 到 n,它们之间通过若干条双向道路连接。
Byteasar 慕名来到了比特镇旅游,不过由于昂贵的门票费,他只能负担起 4 个景区的门票费。他可
以在任意景区开始游览,然后结束在任意景区。
Byteasar 的旅游习惯比较特殊,一旦他路过了一个景区,他就一定会进去参观,并且他永远不会参
观同一个景区两次。所以他想知道,有多少种可行的旅游路线,使得他可以恰好参观 4 个景区呢?即,
有多少条简单路径恰好经过了 4 个点。
Input
第一行包含两个整数 n,表示景区的总数。
第 2 至第 n + 1 行,每行一个长度为 n 的 01 字符串,第 i + 1 行第 j 个字符为 0 表示 i 和 j 之间
没有道路,为 1 表示有一条道路。
输入数据保证 (i, j) 的连接情况等于 (j, i) 的连接情况,且 (i, i) 恒为 0。
Output
输出一行一个整数,即可行的路线总数。

n<=1500

 

记得开long long!!!        记得开long long!!!         记得开long long!!!

40pts(直接枚举第一二三四个点,判断是否重复,是否按顺序连通——O(n^4))

70pts(枚举其中的第一二三个点,第四个点个数就是第三个点的度数-1,当然考虑环的情况,如果第三个点和第一个点连通,要再1   要开long long 比赛的时候因为这个丢掉30分) ——O(n^3)

 

100ptsbitset优化,对于每个类似于a-b-c-d的四元组我们枚举中间的俩元素b-c
    然后对于这一组元素对答案的贡献为(du[b]-1)*(du[c]-1)-包含b-c这组元素的三元环个数
   枚举三元环的个数 ,其中两个元素b-c已经确定,只要再找一个元素x同时满足x-b连通 且x-c连通即可
   然而找到元素x最简单的方法即O(n) 枚举,但是这样的话时间复杂度重新退化为O(n^3)
   于是考虑bitset优化, 假设与b相连的元素集合为s[b],
   则三元环个数即为(s[b]&s[c]).count()(两集合并集中1的个数,即同时与b,c相连)——O(n^3/32)

    本题对于Pascal选手还是不太友好的,但手动bitset也不是不可以/滑稽

 

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<bitset> 
 6 #define int long long //懒人专用 
 7 using namespace std;
 8 inline int read(){
 9     char chr=getchar();    int f=1,ans=0;
10     while(!isdigit(chr)) {if(chr=='-') f=-1;chr=getchar();}
11     while(isdigit(chr))  {ans=(ans<<3)+(ans<<1);ans+=chr-'0';chr=getchar();}
12     return ans*f;
13 }
14 void write(int x){
15     if(x<0) putchar('-'),x=-x;
16     if(x>9) write(x/10);
17     putchar(x%10+'0');
18 }
19 int n,a[1505][1505],du[1505];
20 char s[1505];
21 long long ans=0;
22 bitset<1505> ss[1505];
23 signed main(){
24     freopen("tour.in","r",stdin);
25     freopen("tour.out","w",stdout);
26     n=read();
27     for(register int i=1;i<=n;++i){
28         scanf("%s",s+1);
29         for(register int j=1;j<=n;++j)
30             if(s[j]=='0') a[i][j]=0;else a[i][j]=1,du[i]++;
31     }
32     if(n<=50)//①40pts,这里码风可能不太友好(考试的时候为了卡常)当然了,如果100分方法看懂了可以略过前两种部分分的方法
33     for(register int i=1;i<=n;++i)
34         for(register int j=1;j<=n;++j){if(i!=j&&a[i][j])
35             for(register int k=1;k<=n;++k){if(j!=k&&i!=k&&a[j][k])
36                 for(register int l=1;l<=n;++l){if(i!=l&&j!=l&&k!=l&&a[k][l])++ans;}}}
37     else if(n<=300){
38 //②70pts
39         for(register int i=1;i<=n;++i)
40             for(register int j=1;j<=n;++j){
41                 if(i==j) continue;
42                 if(!a[i][j]) continue;
43                 for(register int k=1;k<=n;++k){
44                     if(i==k||j==k) continue;
45                     if(!a[j][k]) continue;
46                     ans+=du[k]-1;
47                     if(a[k][i]) ans--;
48                 }
49             }
50     }else{
51 //③100pts
52         for(int i=1;i<=n;i++)
53             for(int j=1;j<=n;j++)
54                 if(a[i][j]==0) ss[i][j]=0;else ss[i][j]=1;
55         for(int i=1;i<=n;i++)
56             for(int j=1;j<=n;j++){
57                 if(!a[i][j]) continue;
58                 if(i==j) continue;
59                 ans+=(du[i]-1)*(du[j]-1)-(ss[i]&ss[j]).count();
60             }
61     }
62     write(ans);
63     return 0;
64 }

 Problem C. Walk(walk.c/cpp/pas)

考试妥妥的打暴力系列

在比特镇一共有 n 个街区,编号依次为 1 到 n,它们之间通过若干条单向道路连接。
比特镇的交通系统极具特色,除了 m 条单向道路之外,每个街区还有一个编码 vali,不同街区可能
拥有相同的编码。如果 vali and valj = valj,即 vali 在二进制下与 valj 做与运算等于 valj,那么也会
存在一条额外的从 i 出发到 j 的单向道路。
Byteasar 现在位于 1 号街区,他想知道通过这些道路到达每一个街区最少需要多少时间。因为比特
镇的交通十分发达,你可以认为通过每条道路都只需要 1 单位时间。
Input
第一行包含两个正整数 n, m,表示街区的总数以及道路的总数。
第二行包含 n 个正整数 val1, val2, ..., valn,分别表示每个街区的编码。
接下来 m 行,每行包含两个正整数 ui
, vi,表示一条单向道路,起点为 ui,终点为 vi。
Output
输出 n 行,每行一个整数,其中第 i 行输出到达第 i 个街区的最少时间,如果无法到达则输出 −1

①20分,直接求就好了

②40分,O(n^2)建图,O(n)BFS求最短路

③70分,考虑重新构图,新增1<<15个点,枚举子集(O(n^3)),向点 i 向它所有的子集连一条权值为 0 的有向边,然后在向原图中连回去,可能表达不太对,画个图意会一下//待会儿FFT来说又怪语文老师

④100分,还是新建图,点i 只需要向点 i 去掉某一位的 1 的点连边,这样边的数量得到有效控制

 1 #pragma GCC optimize(2)
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<queue>
 7 #define ll long long
 8 using namespace std;
 9 inline int read(){
10     char chr=getchar();    int f=1,ans=0;
11     while(!isdigit(chr)) {if(chr=='-') f=-1;chr=getchar();}
12     while(isdigit(chr))  {ans=(ans<<3)+(ans<<1);ans+=chr-'0';chr=getchar();}
13     return ans*f;
14 }
15 void write(int x){
16     if(x<0) putchar('-'),x=-x;
17     if(x>9) write(x/10);
18     putchar(x%10+'0');
19 }
20 const int N=1300005;
21 int val[N],cnt;
22 int n,m,ver[N<<1],head0[N<<1],head1[N<<1],nxt[N<<1],dis[N<<1],tot=0,x,y,hd=1,tl=0,q[N];
23 bool vis[N<<1];
24 inline void add1(int x,int y){ver[++tot]=y;nxt[tot]=head1[x];head1[x]=tot;}
25 inline void add0(int x,int y){ver[++tot]=y;nxt[tot]=head0[x];head0[x]=tot;}
26 void Run(int x,int w){
27   if(dis[x]>=0)return;
28   dis[q[++tl]=x]=w;
29   for(int i=head0[x];i;i=nxt[i])     Run(ver[i],w);
30   if(x>=cnt) return;
31   for(int i=0;i<20;i++)if(x>>i&1)    Run(x^(1<<i),w);
32 }
33 int main(){
34     freopen("walk.in","r",stdin);
35     freopen("walk.out","w",stdout);
36     n=read(),m=read();cnt=1<<20;
37     for(int i=1;i<=n;i++)    x=read(),add1(i+cnt,x),add0(x,i+cnt);
38     for(int i=1;i<=m;i++)   x=read(),y=read(),add1(x+cnt,y+cnt);
39     for(int i=1;i<=n+cnt;i++) dis[i]=-1;
40     Run(cnt+1,0);
41     while(hd<=tl)for(int i=head1[x=q[hd++]];i;i=nxt[i])Run(ver[i],dis[x]+1);
42     for(int i=1;i<=n;i++) write(dis[i+cnt]),puts("");
43     return 0;
44 }