【2019牛客暑期ACM集训多校第一场】

1.A题。RMQ+二分。比较easy,不再多说。

#include<bits/stdc++.h>
using namespace std;


const int maxn = 1e5 + 5, lgmaxn = 20;
int n, a[maxn], b[maxn];
static int lg[maxn];

struct RMQ {
	int mx[maxn][lgmaxn];

	RMQ() {//构造函数
		if (lg[2] != 1) {
			for (int i = 2; i < maxn; i++) { //因为lg(1)=0
				lg[i] = (i & -i) == i ? lg[i - 1] + 1 : lg[i - 1];
			}
		}
	}

	void build(int n, int* a) {
		for (int i = 0; i <= n; i++)mx[i][0] = a[i];
		for (int j = 1; j <= lg[n + 1]; j++)
			for (int i = 0; i + (1 << j) - 1 <= n; i++)
				mx[i][j] = min(mx[i][j - 1], mx[i + (1 << (j - 1))][j - 1]);
	}

	int query(int l, int r) {
		int k = lg[r - l + 1];
		return min(mx[l][k], mx[r - (1 << k) + 1][k]);
	}

	int queryminidx(int d)
	{
		if (query(1, d) == query(d, d))return d;
		int l = 1,r = d + 1;
		while (l + 1 < r)
		{
			int mid = (l + r) >> 1;
			if (query(mid, d) < query(d, d))l = mid;
			else r = mid;
		}
		return l;
	}

}r1, r2;


int main()
{
	while (~scanf("%d", &n))
	{
		for (int i = 1; i <= n; i++) scanf("%d", a + i);
		for (int i = 1; i <= n; i++) scanf("%d", b + i);
		r1.build(n, a);
		r2.build(n, b);
		int ans = 0;


		for (int i = 1; i <= n; i++) {
			if (r1.queryminidx(i) == r2.queryminidx(i)) ans = i;
			else break;
		}
		printf("%d\n", ans);
	}
}

2.B题。先搞一下两项,然后每次从后边拿出来一项乘进来,然后继续裂项,可以模拟或者dfs或者直接推完。

#include <bits/stdc++.h>
 
using namespace std;
const int mod = 1e9 + 7;
 
int qpow(int a, int b) {
    int ret = 1;
    while (b) {
        if(b&1)ret = 1ll * ret*a%mod;
        a = 1ll * a*a%mod;
        b >>= 1;
    }
    return ret;
}
 
int rev(int x) {
    return qpow(x, mod - 2);
}
 
struct node {
    int k, a;
};
 
void merge(node a, node b, node&ret1, node&ret2) {
    int mul = 1ll*a.k*b.k%mod;
    int r = rev((1ll * b.a*b.a - 1ll * a.a*a.a%mod + mod) % mod);
    ret1.a = a.a;
    ret1.k = 1ll * mul * r%mod;
    ret2.a = b.a;
    ret2.k = mod - 1ll * mul * r%mod;
}
 
int main() {
    //cout << rev(1120) << endl;
    //cout << rev(120) << endl;
    int n;
    while (cin >> n) {
        vector<node> front, back;
        for (int i = 0; i < n; i++) {
            int a; cin >> a;
            back.push_back(node{ 1,a });
        }
        front.push_back(back.back());
        back.pop_back();
        while (!back.empty()) {
            node sum = back.back();
            sum.k = 0;
 
            for (int i = 0; i < front.size(); i++) {
                node a, b;
                merge(front[i],back.back(),a,b);
                front[i] = a;
                sum.k = (sum.k + b.k) % mod;
            }
            front.push_back(sum);
            back.pop_back();
        }
        int ans = 0;
        for (int i = 0; i < front.size(); i++) {
            // cout << front[i].k << " " << front[i].a << endl;
            ans = (ans + 1ll*front[i].k*rev(2*front[i].a)) % mod;
        }
        cout << ans << endl;
    }
}

3.C题。给定一个n维空间的平面(加了限制),找一个点到这个平面的距离最短。这个题目,其实很简单,但是被搞了,完全没啥必要用拉格朗日乘子。贪心就可以过了。重点说一下这个题目:

                                                          \sum_{i=1}^{n}(p_i-\frac{a_i}{m})^2=\frac{1}{m^2}\sum_{i=1}^{n}(mp_i-a_i)^2

我们首先把分母拿出来,然后分析这个式子,由于:

                                                      \\ \sum_{i=1}^{n}p_i=1\\ \\ \sum_{i=1}^{n}m*p_i=m.

那么就相当于我们现在有n个数据他们的总和为m,然后用他们来消减ai,使得答案最小,那么我们该怎么分配每个ai得到的值呢?

显然,我们可以贪心,我们一定是先把最大的给消减掉,因为它如果很大就会对答案造成很大的贡献,或者我们形象的来说明。

                                                     

 

                         

 

我们把a排序之后,会出现这样的情况。那么我们先把a1给推平,让它和a2一样高

然后如果m还有剩余,就继续推:

直到m被用完,那么会把前k个推成a[k]-r/k.然后,后面的n-k个就没有任何变化,为了好算,同时扩大k 倍,然后就两边各自算一下即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e4 + 10;
ll __gcd(ll a, ll b)
{
	return b == 0 ? a : __gcd(b, a % b);
}
ll a[maxn];
int n;
ll m;
int main()
{
	while (~scanf("%lld%lld", &n, &m))
	{
		for (int i = 1; i <= n; i++)
		{
			scanf("%lld", &a[i]);
		}
		sort(a + 1, a + n + 1, greater<int>());
		ll r = m;
		ll pos = 1;
		while (pos < n)
		{
			if (r < (a[pos] - a[pos + 1]) * pos)break;
			r -= (a[pos] - a[pos + 1]) * pos;
			pos++;
		}
		//结果最后一定是被处理为a[pos]-r/pos
		ll ans = (a[pos] * pos - r) * (a[pos] * pos - r) * pos;
		ll fenmu = m * m * pos * pos;
		for (int i = pos + 1; i <= n; i++)
		{
			ans += a[i] * a[i] * pos * pos;
		}
		ll gcd = __gcd(ans, fenmu);
		ans = ans / gcd;
		fenmu = fenmu/ gcd;
		if (fenmu == 1)
		{
			printf("%lld\n", ans);
		}
		else
		{
			printf("%lld/%lld\n", ans, fenmu);
		}
	}

	return 0;
}

4.E题。DP,但是这个思路有点奇怪,不是很懂,队友AC的。

#include<bits/stdc++.h>
using namespace std;
int n,m,f[4010][2010],mod=1e9+7;
int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        f[0][0]=1;
        for(int i=1; i<=(n+m)*2; i++)
            for(int j=0; j<=m+n; j++)
            {
                if(i-j*2>m||2*j-i>n)
                    f[i][j]=0;
                else
                    f[i][j]=(f[i-1][j]+f[i-1][j-1])%mod;
            }
        cout<<f[(m+n)<<1][m+n]<<endl;
    }
}

5.F题。首先找到可以把三角形面积三等分点是中心。

重心O可以把三角形分割成三个四边形,AEDO,CEOF,ODBF。现在把三角形立起来,考虑AEDO,那么再这个区域内的所有点,都会选择BC作为边(可以证明),计算这个区域内的期望,就相当于计算每个点的势能之和。最后搞一搞就是三角形面积的11倍。

6.H题,线性基的使用。转化一下:等价于求解:\sum count(a_i).count(a_i)表示包含ai并且异或和等于0的所有集合的个数。然后把线性基和非线性基分别计算一下就是答案。对于非线性基每个元素的贡献是相同的,比如有n个元素,线性基含有k个,非线性基含有n-k个。那么每个元素的贡献就是:pow(2,n-k-1).为什么?因为剩下的n-k-1个元素,每个元素都是两种状态,选或者不选,这样会产生一个集合,然后这个集合在线性基内部会有唯一一个集合与之对应,加上这个集合让它异或为0,至于线性基集合内部,直接暴力算即可。

#include<bits/stdc++.h>
using namespace std;
 
#define inf  1000000000
#define mod 1000000007
#define maxn 100005
 
#define pb push_back
#define mp make_pair
#define F first
#define S second
#define pii pair
 
#define debug cout<<"hi"<<endl;
 
typedef long long ll;
typedef long double ld;
 
inline void addmod(int &a,int b){a+=b;if(a>=mod) a-=mod;}
inline void decmod(int &a,int b){a-=b;if(a<0)a+=mod;}
inline void addmod(ll &a,ll b){a+=b;if(a>=mod) a-=mod;}
inline void decmod(ll &a,ll b){a-=b;if(a<0)a+=mod;}
 
/********** show time **********/
 
struct LB
{
    static const ll maxbit=63;
    ll b[maxbit],tot;
 
    void ini()
    {
        tot=0;
        memset(b,0,sizeof(b));
    }
    bool ins(ll x)
    {
        for(ll i=maxbit-1; i>=0; i--)
            if(x&(1ll<<i))
            {
                if(!b[i])
                {
                    b[i]=x;
                    break;
                }
                x^=b[i];
            }
        return x>0;
    }
 
} l1,l2,l3;
 
ll qp(ll a,ll b)
{
    ll res=1;
    while(b)
    {
        if(b&1)
            res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
 
ll n,x;
int main()
{
    while(~scanf("%lld",&n))
    {
        vector<ll>a,b,c;
        ll ans=0;
        l1.ini();
        for(ll i=1; i<=n; i++)
        {
            scanf("%lld",&x);
            if(l1.ins(x))
                a.pb(x);
            else
                b.pb(x);
        }
        ll sz=a.size();
        ans+=(n-sz)*qp(2,n-sz-1)%mod;
 
 
        l3.ini();
        ll cnt=0;
        for(ll i=0;i<b.size();i++)
            if(l3.ins(b[i])) cnt++;
 
        for(ll i=0; i<sz; i++)
        {
            c.clear();
            for(ll j=0; j<sz; j++)
                if(j!=i)
                    c.pb(a[j]);
            ll tem=cnt;
            l2=l3;
            for(ll j=0; j<c.size(); j++)
            {
                if(l2.ins(c[j])) tem++;
            }
            if(!l2.ins(a[i]))
                addmod(ans,qp(2,n-tem-1));
        }
        printf("%lld\n",ans);
    }
}

7.I题,线段树维护DP,队友搞得,还没补。

#include<bits/stdc++.h>
 
using namespace std;
typedef long long ll;
 
#define ml ((l+r)>>1)
#define mr (ml+1)
const ll maxn=1e5+15;
ll add[maxn*2],mx[maxn*2],ls[maxn*2],rs[maxn*2],tot;
 
void push_son(ll&son,ll l,ll r,ll addrt){// 这个函数要注意重写
    if(son==0) {
        son=++tot;
        add[son]=0;
        mx[son]=0;
        ls[son]=0;
        rs[son]=0;
    }
    if(addrt!=0){
        mx[son]+=addrt;
        add[son]+=addrt;
    }
}
 
void push_down(ll rt,ll l,ll r){
    push_son(ls[rt],l,ml,add[rt]);// 这行要注意重写
    push_son(rs[rt],mr,r,add[rt]);// 这行要注意重写
    add[rt]=0;// 这行要注意重写
}
 
void push_up(ll rt,ll l,ll r){
    mx[rt]=max(mx[ls[rt]],mx[rs[rt]]);// 这行要注意重写
}
 
void build(ll&rt,ll l,ll r){
    rt=tot=0;
    push_son(rt,l,r,0);// 这行要注意重写
}
 
void update(ll rt,ll l,ll r,ll ql,ll qr,ll d){//
    if(ql<=l&&r<=qr){// 这行要注意重写
        push_son(rt,l,r,d);//add
        return;
    }
    push_down(rt,l,r);
    if(ml>=ql) update(ls[rt],l,ml,ql,qr,d);
    if(mr<=qr) update(rs[rt],mr,r,ql,qr,d);
    push_up(rt,l,r);
}
 
ll query(ll rt,ll l,ll r,ll ql,ll qr){
    if(ql<=l&&r<=qr) return mx[rt];// 这行要注意重写
    push_down(rt,l,r);
    ll ret=0;// 这行要注意重写
    if(ml>=ql) ret=max(ret,query(ls[rt],l,ml,ql,qr));// 这行要注意重写
    if(mr<=qr) ret=max(ret,query(rs[rt],mr,r,ql,qr));// 这行要注意重写
    return ret;
}
 
struct node{ll x,y,a,b;};
 
int main() {
    ios::sync_with_stdio(false);
    ll n;
    while(cin>>n){
        n+=2;
        vector<node> p(n);
        vector<ll> disc(n);
        for(ll i=0;i<n-2;i++) cin>>p[i].x>>p[i].y>>p[i].a>>p[i].b;
        p[n-2]=node{0ll,ll(-1e18),0ll,0ll};
        p[n-1]=node{0ll,ll(1e18) ,0ll,0ll};
        for(ll i=0;i<n;i++) disc[i]=p[i].y;
        sort(disc.begin(),disc.end());
        disc.erase(unique(disc.begin(),disc.end()),disc.end());
        for(ll i=0;i<n;i++) p[i].y=lower_bound(disc.begin(),disc.end(),p[i].y)-disc.begin()+1;
        sort(p.begin(),p.end(),[](node l,node r){return l.x<r.x;});
        ll rt=0,siz=disc.size();
        build(rt,1,siz);
        vector<ll> idx;
        for(ll i=0;i<n;i++){
            idx.push_back(i);
            if(i==n-1||p[i].x!=p[i+1].x){
                queue<ll>ans;
                for(ll  j:idx) {
                    ll oldval=query(rt,1,siz,p[j].y,p[j].y);
                    ll newval=query(rt,1,siz,1,p[j].y);
                    ans.push(newval-oldval);
                }
                for(ll  j:idx){
                    update(rt,1,siz,p[j].y,p[j].y,ans.front()); ans.pop();
                    if(p[j].y-1>=1)update(rt,1,siz,1,p[j].y-1,p[j].a);
                    if(siz>=p[j].y)update(rt,1,siz,p[j].y,siz,p[j].b);
                }
                idx.clear();
            }
        }
        cout<<query(rt,1,siz,1,siz)<<endl;
    }
}
 
 
/*
 *
2
1 2 1 9
2 1 9 1
1
1 1 2 3
4
1 1 1 5
1 2 2 6
2 1 3 7
2 2 4 8
4
1 2 10 1
2 3 10 1
2 1 10 1
3 2 10 1
2
1 2 1 9
2 1 9 1
1
1 1 2 3
4
1 1 1 5
1 2 2 6
2 1 3 7
2 2 4 8
 
 */

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值