2019 杭电多校第一场 题解
总结:
数据范围问题(int / long long )
题意问题
try a try. 1e4dinic冲冲冲
不要看到过的人暂时比较少就不敢写,@byf
比赛不要划水聊天
题解:
A Blank
题意:题目意思是让你去找一个有n个数的数组,这个数组里面的数只有{0,1,2,3}。然后给你m个条件,每个条件有一组l,r,x,表示在数组l,r区间的有x个不同的数。
问你这样的数组有多少个。
思路:比赛时没想到,,,看题解写的。定义 dp[i][j][k][t] 代表填完前 t 个位置后,{0, 1, 2, 3} 这 4 个数字最后一次出现的位置,排序后为 i, j, k, t(i < j < k < t) 的方案数目,则按照第 t + 1 位的数字的四种选择,可以得到四种转移。对于限制可以按照限制区间的右端点分类,求出 dp[i][j][k][t] 后,找到所有以 t 为区间右端点的限制条件,如果当前状态不满足所有限制条件则不合法,不再向后转移。
参考代码:
#include<bits/stdc++.h> using namespace std; #define pii pair<in,int> #define mkp make_pair #define fi first #define se second #define pb push_back #define mod 998244353 typedef long long ll; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x*f; } void add(int &a,int b){a+=b;if(a>=mod)a-=mod;} const int maxn=110; int T,n,m,ans; int f[maxn][maxn][maxn][2]; vector<pair<int,int> > d[maxn]; int main() { T=read(); while(T--) { int l,r,x; ans=0; n=read();m=read(); for(int i=1;i<=n;i++) { d[i].clear(); d[i].pb(mkp(i,1)); } for(int i=1;i<=m;i++) { l=read(),r=read(),x=read(); d[r].pb(mkp(l,x)); } memset(f,0,sizeof(f)); f[0][0][0][0]=1; for(int cur=1;cur<=n;++cur) { int o=cur&1; for(int i=0;i<=cur;++i) for(int j=i;j<=cur;++j) for(int k=j;k<=cur;++k) f[i][j][k][o]=0; for(int i=0;i<=cur;++i) for(int j=i;j<=cur;++j) for(int k=j;k<=cur;++k) { add(f[j][k][cur-1][o],f[i][j][k][o^1]); add(f[i][k][cur-1][o],f[i][j][k][o^1]); add(f[i][j][cur-1][o],f[i][j][k][o^1]); add(f[i][j][k][o],f[i][j][k][o^1]); } for(int i=0;i<=cur;++i) for(int j=i;j<=cur;++j) for(int k=j;k<=cur;++k) { for(auto pi:d[cur]) { int l=pi.first,r=cur,x=pi.second; if((i>=l)+(j>=l)+(k>=l)+(cur>=l)!=x) f[i][j][k][o]=0; } } } for(int i=0;i<=n;++i) for(int j=i;j<=n;++j) for(int k=j;k<=n;++k) add(ans,f[i][j][k][n&1]); printf("%d\n",ans); } return 0; }
B Operation
题解:https://blog.csdn.net/liufengwei1/article/details/96994017
1 #include<bits/stdc++.h> 2 #define maxl 1000010 3 4 using namespace std; 5 6 int n,m,ans; 7 int a[maxl]; 8 struct LB 9 { 10 int p[31],pos[31]; 11 inline void init() 12 { 13 for(int i=0;i<=30;i++) 14 p[i]=0,pos[i]=0; 15 } 16 inline void insert(int x,int id) 17 { 18 for(int i=30;i>=0;i--) 19 if(x&(1<<i)) 20 { 21 if(!p[i]) 22 { 23 p[i]=x; 24 pos[i]=id; 25 return; 26 } 27 if(id>pos[i]) 28 { 29 swap(x,p[i]); 30 swap(id,pos[i]); 31 } 32 x^=p[i]; 33 } 34 } 35 inline int getmax(int l) 36 { 37 int res=0; 38 for(int i=30;i>=0;i--) 39 if(pos[i]>=l && (res^p[i])>res) 40 res^=p[i]; 41 return res; 42 } 43 }b[maxl]; 44 45 inline void prework() 46 { 47 scanf("%d%d",&n,&m); 48 for(int i=1;i<=n;i++) 49 scanf("%d",&a[i]); 50 for(int i=1;i<=n;i++) 51 { 52 b[i]=b[i-1]; 53 b[i].insert(a[i],i); 54 } 55 } 56 57 inline void mainwork() 58 { 59 int l,r,op,x;ans=0; 60 for(int i=1;i<=m;i++) 61 { 62 scanf("%d",&op); 63 if(op==0) 64 { 65 scanf("%d%d",&l,&r); 66 l=(l^ans)%n+1; 67 r=(r^ans)%n+1; 68 if(l>r) swap(l,r); 69 ans=b[r].getmax(l); 70 printf("%d\n",ans); 71 } 72 else 73 { 74 scanf("%d",&x); 75 x^=ans; 76 a[++n]=x; 77 b[n]=b[n-1]; 78 b[n].insert(a[n],n); 79 } 80 } 81 } 82 83 inline void print() 84 { 85 } 86 87 int main() 88 { 89 int t; 90 scanf("%d",&t); 91 for(int i=1;i<=t;i++) 92 { 93 prework(); 94 mainwork(); 95 print(); 96 } 97 return 0; 98 }
C Milk
Unsolved.
D Vacation
题解:题目给出两个人要去旅行,在他们前面有n辆车,每辆车有长度以及车首部到stopline 的距离以及每辆车的最大速度,后面的车不能超过前面的车。问你他们两个的车首部到达stopline的最短时间。
思路:二分答案,求出最后一辆车停在的位置。
参考代码:
#include<bits/stdc++.h> using namespace std; #define mkp make_pair<int,int> typedef long long ll; const int maxn=2e5+10; double eps=1e-12; ll n; struct Node{ ll l,s,v; } a[maxn]; int check(double x) { double ans=a[n].s-a[n].v*x; for(int i=n-1;i>=0;i--) { if(a[i].s-a[i].v*x<=ans+a[i+1].l) ans+=a[i+1].l; else ans=a[i].s-a[i].v*x; } return ans<=eps; } int main() { while(~scanf("%lld",&n)) { for(int i=0;i<=n;i++) scanf("%lld",&a[i].l); for(int i=0;i<=n;i++) scanf("%lld",&a[i].s); for(int i=0;i<=n;i++) scanf("%lld",&a[i].v); double l=0,r=1e18; while(r-l>eps) { double mid=(l+r)/2; if(check(mid)) r=mid; else l=mid; } printf("%.10f\n",r); } return 0; }
E Path
题解:给你n个点,m条边(有向边),每天变有一定的权值。让你求删掉一些边使得1到 n 的最短路径变大的最小代价(代价是删除的边的边权之和)。
思路:先跑一遍最短路求出1到其他点的最短距离,然后 倒着dfs 求出最短路的DAG的所有边,重新建图,跑最小割就行了。
参考代码:
#include<bits/stdc++.h>///////// #define int long long #define ll long long #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 using namespace std; const int MAXN = 10010; const int INF=0x3f3f3f3f3f3f3f3fll; struct node{ int to; int val; int next; }edge[MAXN*100*2],e[MAXN*200*5]; int ind,pre[MAXN],vis[MAXN],dis[MAXN],pre1[MAXN],ind1; int now[MAXN],S,T; int n,m; void add1(int x,int y,int z) { e[ind1].to = y; e[ind1].val = z; e[ind1].next = pre1[x]; pre1[x] = ind1 ++; } void spfa() { for(int i = 1; i <= n; i++){ dis[i] = INF; vis[i] = 0; } vis[T] = 1; dis[T] = 0; queue<int>q; q.push(T); while(!q.empty()){ int tp = q.front(); q.pop(); vis[tp] = 0; for(int i = pre1[tp]; i != -1; i = e[i].next){ int t = e[i].to; if(dis[t] > dis[tp] + e[i].val){ dis[t] = dis[tp] + e[i].val; if(!vis[t]){ vis[t] = 1; q.push(t); } } } } } void add(int x,int y,int z) { edge[ind].to = y; edge[ind].val = z; edge[ind].next = pre[x]; pre[x] = ind ++; } void dfs1(int rt) { vis[rt] = 1; if(rt == T)return ; for(int i = pre1[rt]; i != -1; i = e[i].next){ int t = e[i].to; if(now[rt] + dis[t] + e[i].val == dis[S]){ now[t] = now[rt] + e[i].val; add(rt,t,e[i].val); add(t,rt,0); if(!vis[t]){ dfs1(t); } } } } int bfs() { memset(vis,-1,sizeof(vis)); queue<int>q; vis[S] = 0; q.push(S); while(!q.empty()){ int tp = q.front(); q.pop(); for(int i = pre[tp]; i != -1; i = edge[i].next){ int t = edge[i].to; if(vis[t] == -1 && edge[i].val){ vis[t] = vis[tp] + 1; q.push(t); } } } if(vis[T] == -1)return 0; return 1; } int dfs(int rt,int low) { int used = 0; if(rt == T)return low; for(int i = pre[rt]; i != -1 && used < low; i = edge[i].next){ int t = edge[i].to; if(vis[t] == vis[rt] + 1 && edge[i].val){ int a = dfs(t,min(low-used,edge[i].val)); used += a; edge[i].val -= a; edge[i^1].val += a; } } if(used == 0)vis[rt] = -1; return used; } int x[MAXN*100],y[MAXN*100],z[MAXN*100]; void Init(int flag) { ind1 = 0; memset(pre1,-1,sizeof(pre1)); for(int i = 1; i <= m; i++){ if(!flag){ add1(y[i],x[i],z[i]); } else { add1(x[i],y[i],z[i]); } } } int32_t main() { int t; scanf("%lld",&t); while(t--){ scanf("%lld%lld",&n,&m); for(int i = 1; i <= m; i++){ scanf("%lld%lld%lld",&x[i],&y[i],&z[i]); } Init(0); S=1,T=n; spfa(); Init(1); ind = 0; memset(now,0,sizeof(now)); memset(pre,-1,sizeof(pre)); dfs1(S); int ans = 0; while(bfs()) { while(1) { int a = dfs(S,INF); if(!a)break; ans += a; } } printf("%lld\n",ans); } return 0; }
F Typewriter
题解:题目的意思我想要得到一个给定字符串,然后刚开始是空串,有两种操作,1:在后面添加任意的一个字符,代价为p, 2:从前面已经出现过的子串里面赋值一段,放在后面,代价为q;
让你求得到给定字符串的最小代价。
思路:对 i 从小到大处理,f[i]: 表示形成前i个字符所需要的最小代价;
然后对于每一个i,维护 使得 s[j : i] ∈ s[1 : j-1] 的最小的 j(s[l : r] 表示子串sl sl+1 ... sr),那么记 f[i] 为输出前 i 个字符的最小代价,则 f[i] = min{f[i-1]+p, f[j-1]+q}。
参考代码:
#include<bits/stdc++.h> using namespace std; #define pii pair<int,int> #define mkp make_pair #define fi first #define se second typedef long long ll; const int maxn=2e5+10; char s[maxn]; ll p,q; struct SAM{ int l[maxn<<1],fa[maxn<<1],nxt[maxn<<1][26]; int cnt,last,rt; ll dp[maxn]; void Init() { cnt=last=rt=1; memset(nxt[1],0,sizeof(nxt[1])); fa[1]=l[1]=0; } int NewNode() { cnt++; memset(nxt[cnt],0,sizeof(nxt[cnt])); fa[cnt]=l[cnt]=0; return cnt; } void Add(int ch) { int p=last,np=NewNode(); last=np; l[np]=l[p]+1; while(p && !nxt[p][ch]) nxt[p][ch]=np,p=fa[p]; if(!p) fa[np]=1; else { int q=nxt[p][ch]; if(l[p]+1==l[q]) fa[np]=q; else { int nq=NewNode(); memcpy(nxt[nq],nxt[q],sizeof(nxt[q])); fa[nq]=fa[q]; l[nq]=l[p]+1; fa[q]=fa[np]=nq; while(nxt[p][ch]==q) nxt[p][ch]=nq,p=fa[p]; } } } int Match(char ch) { return nxt[rt][ch-'a']; } void Search(int len) { while(rt && l[fa[rt]]>=len) rt=fa[rt]; if(rt==0) rt=1; } void work(int len,char ch) { rt=nxt[rt][ch-'a']; if(rt==0) rt=1; Search(len); } ll DP() { Init(); Add(s[0]-'a'); int l=1,r=0,len=strlen(s); dp[0]=p; for(int i=1;i<len;++i) { r++; dp[i]=dp[i-1]+p; while(!Match(s[i]) || (r-l+1)>(i+1)/2&&l<=r) { Add(s[l++]-'a'); Search(r-l); } work(r-l+1,s[i]); if(l<=r) dp[i]=min(dp[i],dp[i-(r-l+1)]+q); } return dp[len-1]; } } sam; int main() { while(~scanf("%s%lld%lld",s,&p,&q)) printf("%lld\n",sam.DP()); return 0; }
G Meteor
Unsolved.
H Desert
Unsolved.
I String
题解:题目意思是给你一个长度为N的字符串,然后给你一个K,让你求满足条件的字典序最小的长度为K的子序列。
对于子序列的要求为:(Li,Ri):表示子序列中对应字符的数量应该在这个区间之间
参考代码:
#include <bits/stdc++.h> using namespace std; const int N=2e5+7; char s[N],ans[N]; int k,n,cnt[N][26],l[26],r[26],used[26],last; vector<int> vec[26]; int main() { while(~scanf("%s%d",s,&k)) { for(int i=0;i<=25;++i) scanf("%d%d",l+i,r+i); n = strlen(s); memset(used,0,sizeof(used)); memset(cnt[n],0,sizeof(cnt[n])); for(int i=0;i<26;++i) vec[i].clear(); for(int i=n-1;i>=0;--i) for(int j=0;j<=25;++j) cnt[i][j]=cnt[i+1][j]+(s[i]=='a'+j); for(int i=0;i<=n-1;++i) vec[s[i]-'a'].push_back(i); vector<int>::iterator head[26]; for(int i=0;i<=25;++i) head[i]=vec[i].begin(); last=-1; bool temp=true; for(int i=0;i<=k-1;++i) { bool f1=0; for(int j=0;j<=25;++j) { if(used[j]==r[j]) continue; while(head[j]!=vec[j].end() && (*head[j])<=last) head[j]++; if(head[j]==vec[j].end()) continue; used[j]++;//加上这个位置字符 bool flag=1; int pos=(*head[j]),sum=0; //判断已经使用的数量加上后面的剩余的数量是否满足题意 for(int t=0;t<=25;++t) { if(cnt[pos+1][t]+used[t]<l[t]) flag=0; sum+=max(l[t]-used[t],0); } if(sum>k-i-1) flag=0; sum=0; //最多能使用的字符数量 for(int t=0;t<=25;++t) sum+=min(cnt[pos+1][t],r[t]-used[t]); if(sum<k-i-1) flag=0; if(!flag) used[j]--; else { ans[i]='a'+j; f1=1; last=pos; break; } } if(!f1) { puts("-1");temp=false;break;} } if(!temp) continue; ans[k]='\0'; printf("%s\n",ans); } return 0; }
J - Kingdom
Unsolved.
K Function
Unsolved.
L Sequence
Unsolved.
M Code
题解:https://blog.csdn.net/liufengwei1/article/details/96900870
#include<bits/stdc++.h> #define maxl 1000010 using namespace std; int n,m,ans; int a[maxl]; struct LB { int p[31],pos[31]; inline void init() { for(int i=0;i<=30;i++) p[i]=0,pos[i]=0; } inline void insert(int x,int id) { for(int i=30;i>=0;i--) if(x&(1<<i)) { if(!p[i]) { p[i]=x; pos[i]=id; return; } if(id>pos[i]) { swap(x,p[i]); swap(id,pos[i]); } x^=p[i]; } } inline int getmax(int l) { int res=0; for(int i=30;i>=0;i--) if(pos[i]>=l && (res^p[i])>res) res^=p[i]; return res; } }b[maxl]; inline void prework() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) { b[i]=b[i-1]; b[i].insert(a[i],i); } } inline void mainwork() { int l,r,op,x;ans=0; for(int i=1;i<=m;i++) { scanf("%d",&op); if(op==0) { scanf("%d%d",&l,&r); l=(l^ans)%n+1; r=(r^ans)%n+1; if(l>r) swap(l,r); ans=b[r].getmax(l); printf("%d\n",ans); } else { scanf("%d",&x); x^=ans; a[++n]=x; b[n]=b[n-1]; b[n].insert(a[n],n); } } } inline void print() { } int main() { int t; scanf("%d",&t); for(int i=1;i<=t;i++) { prework(); mainwork(); print(); } return 0; }
后面的题解慢慢更新~