牛客练习赛60 A—F题解(缺E题)
本蒟蒻这次只过了三题 赛后学习了一下出题人巨佬的标码(码风比我好多了 贴的代码有些是仿出题人)
现在将自己的理解写下来与大家分享
博客相同题解 https://www.cnblogs.com/yurenwuyu/
A
这个题一分析就是每个数字都会与所有数字&一下 (a&a=a)
&字操作是二进制同位都为一才为一 这时解法就变成统计每个二进制位上1的次数
#include<bits/stdc++.h> #define ll long long using namespace std; const ll maxn = 1e5; const ll maxm = 30; ll res; ll arr[maxn + 5], n, cnt[maxm + 5]; int main() { scanf("%d", &n); for (int i = 1; i <= n; ++i)scanf("%d", &arr[i]); for (int i = 1; i <= n; ++i) for (int j = 0; j < 30; ++j) if (arr[i] & (1 << j))cnt[j] += 1;///各位上1的个数 for (int i = 0; i < 30; ++i)res += cnt[i] * cnt[i] * (1 << i); printf("%lld\n", res); return 0; }
B
简单的n2枚举 分析一下可以知道每条边都用了n-2次
#include <bits/stdc++.h> #define ll long long using namespace std; ll n,ans; ll mod=998244353; struct P { ll x,y; }a[110]; ll len(P m,P n) { return abs(m.x-n.x)+abs(m.y-n.y); } int main() { scanf("%lld",&n); for(int i=1;i<=n;++i)scanf("%lld%lld",&a[i].x,&a[i].y); for(int i=1;i<=n;++i) { for(int j=i+1;j<=n;++j) { ans+=(len(a[i],a[j])*(n-2))%mod; } } cout<<ans%mod<<endl; return 0; }
C
这是一个比较基础的dp方程的进阶 从n个物品中选k个
dp[i][j]=dp[i-1][j]+dp[i-1][j-1]
i表示到第几个字符了 j是用了几个字符
dp[i-1][j]是不用这个新加的 dp[i-1][j-1]是用这个新加的
这题的关键点是我们这样计算重复了多少字符(本质不同的解释)
其实仔细一想 我们重复计算就是相当于把 输出前面出现的那个符号在那里又多算了一次
多了哪一部分 这个重复符号上次出现的位置的dp值
#include<bits/stdc++.h> #define ll long long using namespace std; const ll maxn = 1000; const ll mod = 1e9 + 7; ll dp[maxn + 5][maxn + 5]; ll n, k, pre[maxn + 5];///pre记录上次同一个字符的出现位置 char s[maxn + 5]; int main() { scanf("%d %d", &n, &k); scanf("%s", s + 1); dp[0][0] = 1;///n中选0个为1种情况 for (int i = 1; i <= n; ++i) { dp[i][0] = 1; for (int j = 1; j <= i; ++j) { dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1]; if (pre[s[i] - 'a'])dp[i][j] -= dp[pre[s[i] - 'a'] - 1][j - 1]; dp[i][j] %= mod; } pre[s[i] - 'a'] = i; } ll res = dp[n][k]; if (res < 0)res += mod; printf("%lld\n", res); return 0; }
D
这题由于蒟蒻(我)追求梦想用n2枚举过了 但赛后还是写了一下正解方程(n枚举应该可以过)
写这题你需要一个 扩展欧几里得模板(能判断是否有整数解) 然后枚举一个数就把题目变成了一个模板题了 前面是n2枚举 后面大概是正解
#include <bits/stdc++.h> #define ll long long using namespace std; ll a,b,c,k,minl; int main() { cin>>a>>b>>c>>k; minl=min(a,min(b,c)); ll s=k/minl; for(int i=1;i<=s;++i) { for(int j=1;j<=s;++j) { ll z1=k-a*i-b*j; ll z2=k-b*i-c*j; ll z3=k-a*i-c*j; if(z1%c==0){cout<<i<<" "<<j<<" "<<z1/c;return 0;} if(z2%a==0){cout<<z2/a<<" "<<i<<" "<<j;return 0;} if(z3%b==0){cout<<i<<" "<<z3/b<<" "<<j;return 0;} } } return 0; } #include <bits/stdc++.h> #define ll long long using namespace std; ll a,b,c,k; void exgcd(ll a,ll b,ll&x,ll&y) { if(b==0) { x=1; y=0; return; } exgcd(b,a%b,x,y); ll t=x; x=y; y=t-(a/b)*y; } ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b); } int main() { scanf("%lld%lld%lld%lld",&a,&b,&c,&k); for(ll i=0;i<=k/c;i++){ ll d=k-i*c; ll x,y; exgcd(a,b,x,y); ll e=gcd(a,b); if(d%e!=0) continue;///有无解 ll n=d/e; ll m=b/e; x*=n;y*=n; x=(x%m+m)%m; y=(d-x*a)/b;///有无整数解 if(x>=0&&y>=0){ printf("%lld %lld %lld",x,y,i); return 0; } } return 0; }
F
这题作为正在学习计算几何的蒟蒻(我) 那肯定是要学习的
做这题的关键是要注意画图才能理解 代码里大部分有注释
#include<bits/stdc++.h> #define lowbit(x) x&(-x) #define ll long long using namespace std; typedef pair<int,int> pii; const int maxn=1e5+7; const int pi=acos(-1.0); const double eps=1e-9; int read()///快读 { int x=0,f=1;char s=getchar(); for( ;!isdigit(s);s=='-' && (f=-1),s=getchar()); for( ;isdigit(s);x=x*10+s-48,s=getchar()); return x*f; } struct point { int x,y,tag; point( ) { x=y=tag=0; } point( ll _x,ll _y ) { x=_x,y=_y; } bool operator==( const point& rhs ) const { return x==rhs.x && y==rhs.y; } point operator -(const point &rhs ) const { return point(x-rhs.x,y-rhs.y); } point operator+( const point &rhs ) const { return point(x+rhs.x,y+rhs.y); } ll operator * ( const point &rhs ) const { /// 叉积 return 1ll*x*rhs.y-1ll*y*rhs.x; } ll dot( const point &rhs ) const { /// 点积 return 1ll*x+rhs.x+1ll*y*rhs.y; } void get() { x=read(),y=read(); } }O,p[maxn],w1[maxn],w2[maxn],A,B; int n,m1,m2; inline bool cmpA( point a,point b ) { return (a-A)*(b-A)>0; } inline bool cmpB( point a,point b ) { return (a-B)*(b-B)>0; } int c[maxn]; ll ans=0; void add( int x ) { while( x<n ) c[x]++,x+=lowbit(x); } int query( int x ,int tt=0 ) { while( x ) tt+=c[x],x-=lowbit(x);return tt;} void solve( point *t,int m ) { sort(t+1,t+1+m,cmpA);///以A点极角排序 ans+=(1ll*m*(m-1))>>1;///先假设所有点对都能与线段相交 for( int i=1;i<=m;i++ ) t[i].tag=i; sort(t+1,t+m+1,cmpB);///以B点极角排序 memset(c,0,sizeof(c)); for( int i=1;i<=m;i++ )///减去不能与线段相交的点对 { ///这里假设线段点为P、Q 选出来的点对为 A、B 在画图过程中发现 ///选出来的能交的点对 B点要以P排序在A前面而且以Q排序也要在A前面 ///不是就减去 以树状数组解决这个区间问题 ans-=query(t[i].tag); add(t[i].tag); } } int main() { n=read(),A.get(),B.get(); for( int i=1;i<=n;i++ ) p[i].get(); for( int i=1;i<=n;i++ ) { if( (A-p[i])*(B-p[i])>0ll ) w1[++m1]=p[i];///利用叉积把点分为线段上下两部分 else w2[++m2]=p[i]; } solve(w1,m1);///第一种情况 两个点在同一侧 solve(w2,m2); ans+=1ll*m1*m2;///第二种情况 两个点在不同侧 还是一样先假设所有点对都能与线段相交 sort(w1+1,w1+m1+1,cmpA);///一样 以A点极角排序 sort(w2+1,w2+m2+1,cmpA); for( int i=1,bas=1;i<=m1;i++ )///减去不能与线段相交的点对 { ///i 上侧 bas 下侧 ///画图分析 while( bas<=m2 && (w1[i]-A)*(w2[bas]-A)>0 ) ++bas;///叉积判断 ans-=bas-1; } sort(w1+1,w1+m1+1,cmpB);///以B点极角排序 sort(w2+1,w2+m2+1,cmpB); for( int i=1,bas=1;i<=m2;i++ ) { ///一样 while( bas<=m1 && (w2[i]-B)*(w1[bas]-B)>0 ) ++bas; ans-=bas-1; } printf("%lld\n",ans); return 0; }
e题的题解可能在我大佬队友写完后贴出来(这个方向我是真不会).....
看到最后的彩蛋 推荐纯音乐 Like Sunday,Like Rain ^-^