这次没传送门了……题目懒得copy上来。
T1.高维宇宙
这题很明显是二分图匹配,题目中明确说了要配对。
我一开始的建图方法是有问题的,因为违背了二分图的一个性质,就是在同一个点集中的点是互相不能连边的。那么如何解决这个问题呢?我们知道任何一个大于2的质数必然是奇数,所以我们可以把所有的奇数作为一个点集,所有的偶数作为一个点集,若两者之和为质数则连边,之后跑网络流即可。
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<set> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(' ') using namespace std; const int M = 10005; const int INF = 1e9; typedef long long ll; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct node { int from,to,next,v; }e[M]; int n,a[105],ecnt = -1,maxflow,deep[105],head[105],prime[20005],tot,source,sink,cur[105]; int ji[105],ou[105],cnt1,cnt2; bool np[20005]; queue <int> q; void add(int x,int y,int z) { e[++ecnt].to = y; e[ecnt].v = z; e[ecnt].from = x; e[ecnt].next = head[x]; head[x] = ecnt; } void euler() { np[1] = 1; rep(i,2,20000) { if(!np[i]) prime[++tot] = i; for(int j = 1;i * prime[j] <= 20000;j++) { np[prime[j]*i] = 1; if(!(i % prime[j])) break; } } // rep(i,1,30) printf("%d ",prime[i]); } bool bfs(int s,int t) { memset(deep,-1,sizeof(deep)); rep(i,0,sink+1) cur[i] = head[i]; while(!q.empty()) q.pop(); deep[s] = 0,q.push(s); while(!q.empty()) { int k = q.front();q.pop(); for(int i = head[k];i != -1;i = e[i].next) { if(deep[e[i].to] == -1 && e[i].v) deep[e[i].to] = deep[k] + 1,q.push(e[i].to); } } if(deep[t] == -1) return 0; else return 1; } int dfs(int s,int t,int limit) { if(s == t || !limit) return limit; int flow = 0; for(int i = cur[s];i != -1;i = e[i].next) { cur[s] = i; if(deep[e[i].to] != deep[s] + 1) continue; int f = dfs(e[i].to,t,min(limit,e[i].v)); if(f) { e[i].v -= f,e[i^1].v += f; flow += f,limit -= f; if(!limit) break; } } if(!flow) deep[s] = -2333333; return flow; } void dinic(int s,int t) { while(bfs(s,t)) maxflow += dfs(s,t,INF); } int main() { // freopen("prime.in","r",stdin); // freopen("prime.out","w",stdout); memset(head,-1,sizeof(head)); n = read(),source = 0,sink = 101; rep(i,1,n) { a[i] = read(); if(a[i] & 1) ji[++cnt1] = a[i],add(source,cnt1,1),add(cnt1,source,0); else ou[++cnt2] = a[i],add(cnt2+n,sink,1),add(sink,cnt2+n,0); } euler(); rep(i,1,cnt1) rep(j,1,cnt2) if(!np[ji[i] + ou[j]]) add(i,j+n,1),add(j+n,i,0); dinic(source,sink); printf("%d ",maxflow); return 0; }
T2.旅行
这题当时没想出来……只写了30pts宽搜暴力。
这道题的题目描述不是很清晰……既然pppfish要带领一群泡泡怪行走,所以肯定只走一条路径。而且题目都说了可能有多组解……显然不会走很多路径。
既然如此,我们知道所选择的答案区间肯定是连续的,而且左右端点必然是在边中出现过。所以我们可以选择枚举左端点,之后把所有的边按右端点从大到小排序,之后依次加入,注意加入的边必须合法,也就是当前枚举的左端点必然在这条边的l,r之内才能被加入。每加入一次用并查集判断图是否联通,如联通更新即可。
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<set> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(' ') using namespace std; const int M = 10005; const int INF = 1e9; typedef long long ll; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } struct edge { int from,to,next,dl,dr; bool operator < (const edge &g) const { return dr > g.dr; } }e[M]; int n,m,a,b,l,r,ecnt,head[M],maxn,pos = INF,fa[M],kl,ans,kr; bool vis[M]; void add(int x,int y,int l,int r) { e[++ecnt].to = y; e[ecnt].dl = l; e[ecnt].dr = r; e[ecnt].from = x; e[ecnt].next = head[x]; head[x] = ecnt; } int getfa(int x) { if(fa[x] == x) return fa[x]; else return fa[x] = getfa(fa[x]); } bool check() { int r1 = getfa(1),r2 = getfa(n); if(r1 == r2) return 1; else return 0; } int main() { n = read(),m = read(); rep(i,1,m) a = read(),b = read(),l = read(),r = read(),add(a,b,l,r); sort(e+1,e+1+m); rep(i,1,m) { kl = e[i].dl,kr = e[i].dr; rep(j,1,n) fa[j] = j; int r1 = getfa(e[i].from),r2 = getfa(e[i].to); if(r1 != r2) fa[r1] = r2; rep(j,1,m) { if(kl < e[j].dl || kl > e[j].dr) continue; int r1 = getfa(e[j].from),r2 = getfa(e[j].to); if(r1 != r2) fa[r1] = r2; if(check()) { kr = min(kr,e[j].dr); int d = kr - kl + 1; if(d > maxn) maxn = d,pos = kl; else if(d == maxn) pos = min(pos,kl); break; } } } printf("%d ",maxn); rep(i,pos,pos+maxn-1) printf("%d ",i);enter; return 0; }
T3.字典
这题本来是会写的……然而考试的时候没时间写跪了,惨淡爆零。
其实正解很好想,我们要求一个字符串是哪几个字符串的前缀,很容易就想到先建一棵trie树,之后的问题在于,我们怎么维护每个字符串的所得到的答案中的最长全0串。其实我们发现一个0串的长度相当于这个字符串所出现过的相邻两个字符串的编号之差。所以我们可以在建树的时候动态维护差值最大值,之后再访问的时候更新即可。
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<set> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(' ') using namespace std; const int M = 505; typedef long long ll; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') op = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { ans *= 10; ans += ch - '0'; ch = getchar(); } return ans * op; } int cnt = 1,n,m; char s[5000005]; struct trie { int fir,la,p,v; }c[5000005][3]; void insert(int num) { int len = strlen(s),now = 1; rep(i,0,len-1) { int k = s[i] - 'a'; if(!c[now][k].v) c[now][k].v = ++cnt; c[now][k].p = max(c[now][k].p,num-c[now][k].la-1); c[now][k].la = num; now = c[now][k].v; } } int query() { bool flag = 0; int len = strlen(s),now = 1,ans; rep(i,0,len-1) { int k = s[i] - 'a'; if(!c[now][k].v) return n; if(i == len-1) return max(c[now][k].p,n - c[now][k].la); now = c[now][k].v; } } int main() { n = read(),m = read(); rep(i,1,n) scanf("%s",s),insert(i); rep(i,1,m) scanf("%s",s),printf("%d ",query()); return 0; }