【牛客---湖南大学2018年第十四届程序设计竞赛重现赛】
(第三次与中奖擦肩而过?
【A---A game】
思路:不会。
【B:Jump】
题意:有N个人,每个人有两个数字xi和yi,选择其中一个可以使用,现在从1开始数,问最多能数到几。
思路:裸的二分图匹配。对于每个人(看成男生),向数字xi和yi(看成女生)分别连一条边。然后从女生开始吸匈牙利算法。
(ps:有傻瓜写二分+最大流了吗?N这么大,怕是不行额。
#include<cmath> #include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; const int maxn=2000010; int Laxt[maxn],Next[maxn],To[maxn],cnt,N,M,T; int linker[maxn],vis[maxn]; void add(int u,int v){ Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; } bool find(int u){ for(int i=Laxt[u];i;i=Next[i]){ int v=To[i]; if(vis[v]!=T){ vis[v]=T; if(!linker[v]||find(linker[v])){ linker[v]=u; return true; } } } return false; } int main() { int x,y,i,ans=0; scanf("%d",&N); for(int i=1;i<=N;i++){ scanf("%d%d",&x,&y); add(x,i); add(y,i); } for(i=1;i<=10000;i++){ T=i; if(find(i)) ans++; else break; } cout<<ans<<endl; return 0; }
【C:A+B】
裸的高精度加法。
#include<bits/stdc++.h> using namespace std; const int maxn=5010; char a[maxn],b[maxn]; int A[maxn],B[maxn],C[maxn]; int main() { int T,L1,L2,L3,i,j; scanf("%d",&T); while(T--){ scanf("%s%s",a+1,b+1); L1=strlen(a+1); L2=strlen(b+1); L3=max(L1,L2)+2; for(i=L1;i>=1;i--) A[L1+1-i]=a[i]-'0'; for(i=L1+1;i<=L3;i++) A[i]=0; for(i=L2;i>=1;i--) B[L2+1-i]=b[i]-'0'; for(i=L2+1;i<=L3;i++) B[i]=0; for(i=1;i<=L3;i++) C[i]=A[i]+B[i]; for(i=1;i<=L3;i++) C[i+1]+=C[i]/10,C[i]%=10; for(i=L3;i>1;i--) if(C[i]!=0) break; for(;i>=1;i--) cout<<C[i]; cout<<endl; } return 0; }
【D:Largest Circle】
题意:给定一个N凸多边形,问最大内接圆的半径。
思路:poj有原题,但是数据变大了(所以我直接抄代码T了。
(几何题,不会
【E:Let's brute force RSA】(拿了一血)
题意:除了题面长,每什么难度。题目以及给出了解法:就是给定N,找到两个素数p,q。满足p*q=N;然后得到L=(p-1)*(q-1)。然后找E关系L的逆元。
思路:扩欧求逆元。而用D=pow(E,L-2)是错的,因为E和L没有保证互质。
#include<bits/stdc++.h> using namespace std; #define ll long long const int maxn=1000000; int p[maxn+10],vis[maxn+10],cnt; void solve() { for(int i=2;i<=maxn;i++){ if(!vis[i]) p[++cnt]=i; for(int j=1;j<=cnt&&p[j]*i<=maxn;j++){ vis[i*p[j]]=1; if(i%p[j]==0) break; } } } void extgcd(ll a,ll b,ll& d,ll& x,ll& y){ if(!b){ d=a; x=1; y=0;} else{ extgcd(b,a%b,d,y,x); y-=x*(a/b); } } ll inverse(ll a,ll n){ ll d,x,y; extgcd(a,n,d,x,y); return d==1?(x+n)%n:-1; } int main() { solve(); int T,i,j; ll E,D,N,pp,qq,L; scanf("%d",&T); while(T--){ scanf("%lld%lld",&E,&N); for(i=1;i<=cnt;i++){ if(N%p[i]==0&&!vis[N/p[i]]){ pp=p[i]; qq=N/p[i]; break; } } L=(pp-1)*(qq-1); D=inverse(E,L); cout<<D<<" "<<N<<endl; } return 0; }
【F:Similarity】(字符串,本来也是一血。。。emm,小失误)
题意:给定字符串S,T。求所有T的字串在S中出现的的次数和。
思路:后缀自动机求出S的每个字串以及拓扑得到每个字串的出现次数。然后T串在S上面跑。一直累加。
(比赛的时候错误原因是,在自动机上跑的时候没有模拟T串的长度。
(然后注意记忆话搜索或者加lazy。
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; #define ll long long const int maxn=1000010; ll ans,dp[maxn]; char c[maxn]; struct Sam { int fa[maxn],ch[maxn][26],maxlen[maxn],Last,cnt; int a[maxn],b[maxn],num[maxn],np,nq,p,q; Sam(){ Last=cnt=1; } void add(int x) { np=++cnt; p=Last; Last=np; maxlen[np]=maxlen[p]+1; num[np]++; //方法1 while(p&&!ch[p][x]) ch[p][x]=np,p=fa[p]; if(!p) fa[np]=1; else { q=ch[p][x]; if(maxlen[p]+1==maxlen[q]) fa[np]=q; else { nq=++cnt; maxlen[nq]=maxlen[p]+1; fa[nq]=fa[q]; fa[q]=fa[np]=nq; memcpy(ch[nq],ch[q],sizeof(ch[nq])); while(p&&ch[p][x]==q) ch[p][x]=nq,p=fa[p]; } } } ll get(int tp) { if(tp<=1) return 0; if(dp[tp]) return dp[tp]; dp[tp]=(ll)num[tp]*(maxlen[tp]-maxlen[fa[tp]]); dp[tp]+=get(fa[tp]); return dp[tp]; } void sort() { for(int i=1;i<=cnt;i++) a[maxlen[i]]++; for(int i=1;i<=cnt;i++) a[i]+=a[i-1]; for(int i=1;i<=cnt;i++) b[a[maxlen[i]]--]=i; for(int i=cnt;i>=0;i--) num[fa[b[i]]]+=num[b[i]]; } void solve() { scanf("%s",c+1); int L=strlen(c+1),Len=0; p=1; //记住模拟长度。。。 for(int i=1;i<=L;i++){ int x=c[i]-'a'; if(ch[p][x]) Len++, p=ch[p][x]; else { while(p&&!ch[p][x]) p=fa[p]; if(!p) Len=0,p=1; else Len=maxlen[p]+1,p=ch[p][x]; } int tp=p; ans+=(ll)(Len-maxlen[fa[tp]])*num[tp]; tp=fa[tp]; ans+=get(tp); } } }sam; int main() { scanf("%s",c+1); int L=strlen(c+1); for(int i=1;i<=L;i++) sam.add(c[i]-'a'); sam.sort(); sam.solve(); printf("%lld\n",ans); return 0; }
【I:Line K coverage】
题意:给定N个(N<30)居民点,每个点可以自己安装WIFI,价格是Wi,或者在某些点建立半径为R的WIFI发射器,价格是Ci,在发射器范围内的居民使用wifi不需要钱。现在最多可以建立K个WIFI发射器,问最小费用。
思路:DP做,dp[i][k]表示以i是最后一个发射器,前面i个居民点一共建立了k个发射器的最小花费。
那么dp[i][k]=min(dp[j][k-1]+(j+1到i-1没有被j处的发射器和k处的发射器覆盖的居民点的安装WIFI的费用。),...)
最后我们把每个dp[][]加上对于末尾没有覆盖到的费用,来更新答案。
(还未AC的代码:
#include<bits/stdc++.h> using namespace std; const int maxn=40; const int inf=1e9+7; struct in{ int x,w,c; }s[maxn]; bool cmp(in w,in v){ return w.x<v.x; } int dp[maxn][maxn]; int cal(int bg,int ed,int L,int R) { int res=0; for(int i=bg;i<=ed;i++) if(s[i].x>L&&s[i].x<R) res+=s[i].w; return res; } int main() { int N,K,R,i,j,k,tmp; while(~scanf("%d%d%d",&N,&K,&R)){ for(i=1;i<=N;i++) scanf("%d%d%d",&s[i].x,&s[i].w,&s[i].c); sort(s+1,s+N+1,cmp); K=min(N,K); for(i=1;i<=N;i++) for(j=1;j<=N;j++) dp[i][j]=inf; int ans=0; for(i=1;i<=N;i++) ans+=s[i].w; for(i=1;i<=N;i++){ for(k=1;k<=i;k++) { if(k==1){ tmp=s[i].c+cal(1,i,-inf,s[i].x-R); dp[i][k]=min(dp[i][k],tmp); } else { for(j=1;j<i;j++) { tmp=s[i].c+dp[j][k-1]+cal(j+1,i,s[j].x+R,s[i].x-R); dp[i][k]=min(dp[i][k],tmp); } } } } for(i=1;i<=N;i++) for(k=1;k<=i;k++) ans=min(ans,dp[i][k]+cal(i+1,N,s[i].c+R,inf)); printf("%d\n",ans); } return 0; }
【G:Max Convolution】
。。。