【寒假集训系列DAY.1】
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)
③100pts (bitset优化,对于每个类似于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 }