Gym 101889:2017Latin American Regional Programming Contest(寒假自训第14场)
昨天00.35的CF,4点才上床,今天打的昏沉沉的,WA了无数发。 题目还是满漂亮的。 尚有几题待补。
C .Complete Naebbirac's sequence
题意:给定N个数,他们在1到K之间,现在1到K的出现次数的不完全相同的,现在让你进行一次操作,使得他们相同。 操作是加一个数到集合; 或者删去一个数; 或同时一个数,删一个数。
思路:枚举三种情况即可。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=200010; int a[maxn],num[maxn],N,M; bool checkdel() { int cnt=0,x=0; if((N-1)%M==0) { rep(i,1,M) if(num[i]==(N-1)/M+1) x=i; else if(num[i]==(N-1)/M) cnt++; } else return false; if((cnt==M-1)&&x) { printf("-%d\n",x); return true; } return false; } bool checkadd() { bool F=true; int x=0,cnt=0; if((N+1)%M!=0) return false; rep(i,1,M) if(num[i]==(N+1)/M) cnt++; else if(num[i]==(N+1)/M-1)x=i; if((cnt==M-1)&&x){ printf("+%d\n",x); return true; } return false; } bool checkadc() { bool F=true; int cnt=0,x=0,y=0; if(N%M!=0) return false; rep(i,1,M) { if(num[i]==N/M) cnt++; else if(num[i]>N/M) x=i; else if(num[i]<N/M) y=i; } if(cnt==M-2&num[x]==num[y]+2) { printf("-%d +%d\n",x,y); return true; } return false; } int main() { scanf("%d%d",&M,&N); rep(i,1,N) scanf("%d",&a[i]),num[a[i]]++; if(checkadd()) return 0; if(checkadc()) return 0; if(checkdel()) return 0; puts("*"); return 0; }
D .Daunting device
题意:N个格子排出一排,开始格子颜色都是1;现在有M个操作: N,M<1e5
或,把区间[L,R]颜色改为c; 或,查询一共有多少格子颜色为c。 最后求颜色最多的数量。
数据是随机的,且强制在线。
思路:ODT裸题。维护相同颜色的区间。注意split的时候先split(R+1),再split(L);因为这个我wa20了;
在这里有写。 大概就是会改变指针啥的,我对iterator不了解,所以不知道,记下来好了。
开始想的分块也能做,但是我感觉还带个log。N*sqrt*log,数据水的话没准可以。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; struct in{ int L,R,v; in(){} in(int LL,int RR,int vv):L(LL),R(RR),v(vv){} friend bool operator<(in a,in b){ if(a.L==b.L) return a.R<b.R; return a.L<b.L; } }; set<in>s; #define IT set<in>::iterator int num[100010],ans; IT split(int pos) { IT it=s.lower_bound(in(pos,-2,-2)); if(it!=s.end()&&(*it).L==pos) return it; it--; //if(pos>it->R) return s.end(); int l=(*it).L,r=(*it).R,v=(*it).v; s.erase(it); s.insert(in(l,pos-1,v)); return s.insert(in(pos,r,v)).first; } void Assign(int L,int R,int X) { IT it2=split(R+1),it1=split(L); for(IT it=it1;it!=it2;it++) { num[(*it).v]-=((*it).R-(*it).L+1); } s.erase(it1,it2); s.insert(in(L,R,X)); num[X]+=R-L+1; } int main() { int N,L,C,P,X,A,B,S; scanf("%d%d%d",&L,&C,&N); s.insert(in(0,L-1,1)); num[1]=L; rep(i,1,N){ scanf("%d%d%d%d",&P,&X,&A,&B); A%=L; B%=L; S=num[P]%L; int l=(A+1LL*S*S%L)%L,r=(A+1LL*(S+B)*(S+B)%L)%L; Assign(min(l,r),max(l,r),X); } rep(i,1,C) ans=max(ans,num[i]); printf("%d\n",ans); return 0; }
E .Enigma
题意:给定一个字符串c[]和整数N,,或者是'?',或者是数字, '?'表示你可以替换为数字。 让你求字典序最小的c,满足他是N的倍数, |c|,N<1000;无解输出"*";
思路:显然是DP,dp[i][j]表示前i位%N是否可以为j; 开始时,dp[0][0]=1; 然后做背包,最后倒着来,就可以得到最小字典序了。O(N^2);
注意第一位不能为0;
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) #define rep2(i,a,b) for(int i=a;i>=b;i--) using namespace std; const int maxn=1010; char c[maxn]; int dp[maxn][maxn],N,L,fac[maxn]; int main() { scanf("%s%d",c+1,&N); L=strlen(c+1); fac[0]=1%N; rep(i,1,L) fac[i]=fac[i-1]*10%N; dp[L+1][0]=1; rep2(i,L,1){ if(c[i]=='?'){ rep(k,0,9) rep(j,0,N-1) dp[i][(j+k*fac[L-i])%N]|=dp[i+1][j]; } else { rep(j,0,N-1) dp[i][(j+(c[i]-'0')*fac[L-i])%N]|=dp[i+1][j]; } } if(!dp[1][0]) puts("*"); else{ int now=0; rep(i,1,L){ if(c[i]!='?'){ putchar(c[i]); now=((now-(c[i]-'0')*fac[L-i])%N+N)%N; } else{ rep(j,0,9){ if(i==1&&j==0) continue; if(dp[i+1][((now-j*fac[L-i])%N+N)%N]) { printf("%d",j); now=((now-j*fac[L-i])%N+N)%N; break; } } } } } return 0; }
F .Fundraising
题意:现在有N个慈善家,每个人有ai和bi属性,以及准备捐的款ci。有矛盾的慈善家不能同时出场,两个人有矛盾,当前仅当(ai-aj)*(bi-bj)<0;问怎么安排出场捐款最大。
思路:典型的一维排序,一维用线段树或者树状数组更新DP值。
注意有两个人的a和b完全相同的情况,我们把ci小的那个人删去。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; struct in{ int b,f;ll d; friend bool operator <(in w,in v){ if(w.b!=v.b) return w.b<v.b; return w.f>v.f; } }s[maxn]; ll Mx[maxn],res[maxn],ans; int b[maxn],tot; void update(int Now,int L,int R,int pos,ll val) { if(L==R){Mx[Now]=max(Mx[Now],val); return ;} int Mid=(L+R)>>1; if(pos<=Mid) update(Now<<1,L,Mid,pos,val); else update(Now<<1|1,Mid+1,R,pos,val); Mx[Now]=max(Mx[Now<<1|1],Mx[Now<<1]); } ll query(int Now,int L,int R,int l,int r) { if(l<=L&&r>=R) return Mx[Now]; int Mid=(L+R)>>1; ll res=0; if(l<=Mid) res=max(res,query(Now<<1,L,Mid,l,r)); if(r>Mid) res=max(res,query(Now<<1|1,Mid+1,R,l,r)); return res; } int main() { int N; scanf("%d",&N); rep(i,1,N){ scanf("%d%d%lld",&s[i].b,&s[i].f,&s[i].d); b[++tot]=s[i].f; } sort(b+1,b+tot+1); tot=unique(b+1,b+tot+1)-(b+1); rep(i,1,N) s[i].f=lower_bound(b+1,b+tot+1,s[i].f)-b; sort(s+1,s+N+1); rep(i,1,N-1) { if(s[i].f==s[i+1].f&&s[i].b==s[i+1].b) s[i+1].d+=s[i].d,s[i].d=0; } sort(s+1,s+N+1); rep(i,1,N) { int j=i; while(j+1<=N&&s[j].b==s[j+1].b) j++; rep(k,i,j) { if(s[k].f>1)res[k]=query(1,1,tot,1,s[k].f-1); res[k]+=s[k].d; ans=max(ans,res[k]); } rep(k,i,j) update(1,1,tot,s[k].f,res[k]); i=j; } printf("%lld\n",Mx[1]); return 0; }
H .Hard choice
by .pb 应该是个水题,懒得看了
#include<bits/stdc++.h> #define ll long long using namespace std; int a,b,c,x,y,z; template<class T> inline void read(T&a){ char c=getchar(); for(a=0;(c<'0'||c>'9')&&c!='-';c=getchar()); bool f=0;if(c=='-')f=1,c=getchar(); for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0'; if(f)a=-a; } int main(){ read(a),read(b),read(c),read(x),read(y),read(z); printf("%d\n",max(0,x-a)+max(y-b,0)+max(z-c,0)); return 0; }
I .Imperial roads
题意:给定N点M边带权连通图,现在Q次询问,每次问在必选某条边的情况下,最小生成树的大小。
思路:先建一个MST,大小为sum, 如果询问边已经在树上,那么答案是sum; 否则,ans=sum-len+maxedge(u->lca->v);
即是用环里最大替换掉。。。老掉牙的基础题了,可以用树剖或者倍增求路径最大边权。
#include<bits/stdc++.h> #define pii pair<int,int> #define rep(i,a,b) for(int i=a;i<=b;i++) #define rep2(i,a,b) for(int i=a;i>=b;i--) #define ll long long using namespace std; const int maxn=200010; struct in{ int u,v,len; friend bool operator<(in a,in b){ return a.len<b.len; } }s[maxn]; int fa[maxn],dep[maxn],f[maxn][21],Mx[maxn][21]; int Laxt[maxn],Next[maxn<<1],To[maxn<<1],Len[maxn<<1],cnt; map<pii,int>mp; void add(int u,int v,int w){ Next[++cnt]=Laxt[u]; Laxt[u]=cnt;To[cnt]=v;Len[cnt]=w; } int find(int x){ if(x==fa[x]) return x; return fa[x]=find(fa[x]); } void dfs(int u,int pre) { f[u][0]=pre; dep[u]=dep[pre]+1; for(int i=Laxt[u];i;i=Next[i]){ if(To[i]!=pre){ dfs(To[i],u); Mx[To[i]][0]=Len[i]; } } } int LCA(int u,int v) { if(dep[u]<dep[v]) swap(u,v); rep2(i,20,0) if(dep[f[u][i]]>=dep[v]) u=f[u][i]; if(u==v) return u; rep2(i,20,0) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i]; return f[u][0]; } int query(int u,int v) { int Lca=LCA(u,v),res=0; rep2(i,20,0) if(dep[f[u][i]]>=dep[Lca]) { res=max(res,Mx[u][i]);u=f[u][i]; } rep2(i,20,0) if(dep[f[v][i]]>=dep[Lca]) { res=max(res,Mx[v][i]);v=f[v][i]; } return res; } int main() { int N,M,Q,u,v,sum=0; scanf("%d%d",&N,&M); rep(i,1,N) fa[i]=i; rep(i,1,M){ scanf("%d%d%d",&s[i].u,&s[i].v,&s[i].len); if(s[i].u>s[i].v) swap(s[i].u,s[i].v); mp[pii(s[i].u,s[i].v)]=s[i].len; } sort(s+1,s+M+1); rep(i,1,M){ int fu=find(s[i].u),fv=find(s[i].v); if(fu!=fv){ mp[pii(s[i].u,s[i].v)]=0; fa[fu]=fv; add(s[i].u,s[i].v,s[i].len); add(s[i].v,s[i].u,s[i].len); sum+=s[i].len; } } dfs(1,0); rep(i,1,20) rep(j,1,N){ f[j][i]=f[f[j][i-1]][i-1]; Mx[j][i]=max(Mx[j][i-1],Mx[f[j][i-1]][i-1]); } scanf("%d",&Q); rep(i,1,Q){ scanf("%d%d",&u,&v); if(u>v) swap(u,v); if(mp[pii(u,v)]==0) printf("%d\n",sum); else printf("%d\n",sum+mp[pii(u,v)]-query(u,v)); } return 0; }
J .Jumping frog
题意:给定一个石头和水坑组成的环,顺时针依次是0到N-1;现在问对于K=[1,N-1],有多少个K满足,至少有一个点作为起点,每次顺时针跳到第K个点上,直到回到出发点,这个过程中不会跳到水坑中。
思路:对于K,我们直到他遍历的点和起点的距离的N/gcd(N,K)的倍数。 所以我们可枚举N/gcd(N,K);这个数是N的因子,数量比较少,对于每个因子,我们去暴力找即可。
还可以优化:如果k满足,那么k的倍数也满足。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=100010; char c[maxn]; int fac[maxn],vis[maxn],tot,N,ans; bool judge(int bg,int L) { int Now=bg; while(true){ if(c[Now]=='P') return false; Now=Now+L; if(Now>=N) Now-=N; if(Now==bg) break; } return true; } bool check(int L) { rep(i,0,L-1) if(judge(i,L)) return true; return false; } int main() { scanf("%s",c); N=strlen(c); rep(i,1,N-1) if(N%i==0) fac[++tot]=i; rep(i,1,tot) if(check(fac[i])) vis[fac[i]]=1; rep(i,1,N-1) ans+=vis[__gcd(i,N)]; printf("%d\n",ans); return 0; }