Codeforces Round #538 (Div. 2) / codeforces1114ABCDEF

Codeforces Round #538 (Div. 2) / codeforces1114ABCDEF

传送门

A Got Any Grapes?

B Yet Another Array Partitioning Task

C  Trailing Loves (or L'oeufs?)

D Flood Fill

E Arithmetic Progression

F Please, another Queries on Array?

 


Got Any Grapes?

判断一下即可

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

typedef long long ll;
typedef pair<ll,ll>  P;
//vector<vector<ll> >v;
const int maxn = 2e6 + 5000;
ll  n,m;
//ll a[maxn];
ll a,b,c,x,y,z;
int vis[maxn];
int xx[4] = {1,-1,1,-1};
int yy[4] = {-1,1,1,-1};
priority_queue<int,vector<int>,greater<int> >q;
ll ans ;
int main() {
    while(cin >> x >> y >> z >> a >> b >> c) {
        if(a >= x && b + a - x >= y && b + (a - x) - y + c >= z) {
                    cout << "YES" << endl;
                    continue;
        }
        cout << "NO" << endl;
    }
    return 0;
}

Yet Another Array Partitioning Task

给你n个价值,要求分为k段,并且每段求前m大个数,加起来,问最后相加起来sum最大是多少

可以知道,分为k段,取出每段前m个加起来,所以直接排序,求前m * k个便是最大sum,之后

因为需要列出分组情况,标记一下就可以了

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

typedef long long ll;
typedef pair<ll,ll>  P;
//vector<vector<ll> >v;
const int maxn = 2e6 + 5000;
ll  n,m,k;
ll a[maxn];
int vis[maxn];
int xx[4] = {1,-1,1,-1};
int yy[4] = {-1,1,1,-1};
priority_queue<pair<ll,ll> >q;
ll ans ;
struct node {
    int x,id;
} g[maxn];


int main() {
    while(cin >> n >> m >> k ) {
        ll sum = 0;
        int num = 0,num1 = 0;
        memset(vis,0,sizeof(vis));
        while(!q.empty()) q.pop();
        for(int i = 1; i <= n; i++) {
            pair<int,int>p;
            cin >> p.first;
            p.second = i;
            q.push(p);
        }
        for(int i = 1; i <= m * k; i++) {
            ans += q.top().first;
            vis[q.top().second] = 1;
            q.pop();
        }
        cout << ans << endl;
        int cnt = 0 ;
        for(int i = 1; i < n; i++) {
            num += vis[i];
            if(num == m) {
                cout << i << " ";
                num = 0;
                cnt++;
            }
            if(cnt == k - 1) break;
        }
        cout << endl;
    }
    return 0;
}

C  Trailing Loves (or L'oeufs?)

给一个n和b,查询n!在b进制下末尾0的个数

很容易可以想到把b分解质因子,然后对于每个b的质因子,去计数一下看看当前质因子在x中有多少个

另外还要除以一下b是由多少个当前这个质因子构成,对于每个质因子算出来的结果取个min即可

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

typedef long long ll;
typedef pair<ll,ll>  P;
//vector<vector<ll> >v;
const int maxn = 2e6 + 5000;
ll  n,m,k;
ll a[maxn];
int vis[maxn];
int xx[4] = {1,-1,1,-1};
int yy[4] = {-1,1,1,-1};
priority_queue<pair<ll,ll> >q;
ll ans ,b;
struct node {
    int x,id;
} g[maxn];

ll res(ll x,ll N){
    ll sum = 0;
    while(N){
        sum += N / x, N /= x;
    }
    return sum ;
}
int main() {
    while(cin >> n >> b) {
            ans = 1e18;
        for(ll i = 2;i * i <= b;i++){
            if(b % i == 0){
                ll num = 0;
                while(b % i == 0) b /= i,num++;
                ans = min(ans,res(i,n) / num);
            }
        }
        if(b != 1) ans = min(ans,res(b,n));
         cout << ans << endl;
    }
    return 0;
}

Flood Fill

n个数,每次使得l到r区间(前提l到r区间所有数必须相等)中的数,变成任意一个数,并且如果变换开始,

那么只能从l到r一直扩增,不能再由其他位置改变,当时没翻译对这个地方,怎么想也想不到做法 哎

如果知道刚刚上个改变方式,对于5000的范围很容易可以想到区间dp,但是由于不能n^3,不需要枚举断点

因为对于一次只能从l到r往其他地方扩展,所以直接枚举区间

有三种状态

对于l + 1到r - 1区间:

如果a[l] == a[r] 那么f[l][r] = f[l + 1][r - 1] + 1,因为很明显只用改变l+1到r-1的数变成a[l]就可以了

如果a[i] != a[r] ,那么有两种状态:f[l][r] = min(f[l + 1][r],f[l][r - 1]) + 1,即先改变l和先改变r取一个最优的

可以写枚举形式,我是直接把区间dp写成记忆化搜索了。

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

typedef long long ll;
typedef pair<ll,ll>  P;
//vector<vector<ll> >v;
const int maxn = 2e6 + 5000;
ll  n,m,k;
ll a[maxn];
int vis[maxn];
int xx[4] = {1,-1,1,-1};
int yy[4] = {-1,1,1,-1};
priority_queue<pair<ll,ll> >q;
ll ans ,b;
struct node {
    int x,id;
} g[maxn];

ll res(ll x,ll N){
    ll sum = 0;
    while(N){
        sum += N / x, N /= x;
    }
    return sum ;
}
                vector<int>v;
int f[5500][5500];
int dfs(int l,int r){
    if(f[l][r] != -1) return f[l][r];
    if(l == r) return 0;
    int ans;
    if(v[l] == v[r]) return f[l][r] = dfs(l + 1,r - 1) + 1;
    else return f[l][r] = min(dfs(l + 1,r),dfs(l,r - 1)) + 1;

}
int main() {
    while(cin >> n) {
            v.clear();
        for(int i = 1;i <= n;i++)  cin >> a[i];
        v.push_back(a[1]);
        for(int i = 2;i <= n;i++){
            if(a[i] == a[i - 1]) continue;
            else v.push_back(a[i]);
        }
//        for(auto d:v){
//            cout << d <<endl;
//        }
       memset(f,-1,sizeof(f));
       cout << dfs(0,v.size() - 1) << endl;

    }
    return 0;
}

Arithmetic Progression

交互题:有一个等差数列,要求你找到首项和公差d

你可以有60次操作

q1 : 查询等差数列中第i项的值

q2:查询等差数列中是否存在比x更大的值

交互题一般不是二分就是构造(个人感觉),再不行就是二分加个构造

所以你可以先二分出这个等差数列的上界是多大,因为范围在1e9之间,所以基本上30次就可以二分出边界最大值,

接着去用剩下的次数去查询第i项的值,用最大值gcd = __gcd(Max - a[i],gcd) 变可以得到公差,i可以自己写个随机

函数随机一下,而首项的值就等于Max - (n - 1) * d

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

typedef long long ll;
typedef pair<ll,ll>  P;
vector<vector<ll> >v;
const int maxn = 2e6 + 5000;
ll  n,m,ks;
ll a[maxn];
bool vis[maxn];
string str[maxn];
int xx[4] = {1,-1,1,-1};
int yy[4] = {-1,1,1,-1};
int cnt = 60;
int query1(int x){
    int val;
    cnt--;
    cout << "> "  << x << endl;
    cin >> val;
    return val;
}
int query2(int x){
    int val;
    cout << "? " << x << endl;
    cin >> val;
    return val;
}
int erfen(int l,int r){
    int x ;
    while(l <= r){
        int mid = (l + r) / 2;
        if(query1(mid)){
            l = mid + 1;
        }else{
            r = mid - 1,x = mid;
        }
    }
    return x;
}
long long  Rand(double s,double t) {
    return s + 1.0*rand()/RAND_MAX*(t-s);
}
int main() {
    cin >> n;
    srand((unsigned)time(NULL));
    int Max = erfen(1,1e9);
    int d = 0;
    for(int i = 1;i <= cnt;i++){
        int num = Rand(1,n + 1);
        int dd = query2(num);
        d = __gcd(d,Max - dd);
    }
    cout << "! "<< Max - (n - 1) * d << " " << d << endl;
    return 0;
}

Please, another Queries on Array?

有n个数,两种操作

q1:让l到r区间 * x

q2:查询l到r区间欧拉函数值的乘积

很明显线段树,但是有好多细节

考到的东西还是不少的,

首先对于300里有大约62个素数(如果没记错的话),压位一下,建一棵线段树,然后保存一下乘积和当前这个数与这62个素数或的状态,也放进去,对于q1操作我们需要做的就是转换一下x得到压位后的状态码,然后再l到r区间上乘x,再或上x的状态码,

查询l到r的欧拉函数乘积的时候,需要得到l到r的状态码,即可以知道l到r有那些素数,也可以得到l到r的乘积,直接算是不可以的,我们需要预处理一下62个素数欧拉函数值,另外因为需要mod1e9+7,还要用一下逆元即可

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

typedef long long ll;
typedef pair<int,int>  P;
const int maxn = 4e5 + 5000;
int n,m;
const ll mod = 1e9 + 7;
int sum = 0;
ll Stauts[maxn];
int prime[5000];
bool isprime[5000];
int cnt = 1;
ll a[maxn];

struct node {
    int l,r;
    int valu;//值
    int lazy;//懒惰乘积
    ll Statuslazy;//往下传递的
    ll Status;//自己的
} no[maxn<<2];
ll f[65];
ll qpow(ll x,ll y) {
    ll ans=1;
    while(y) {
        if(y&1)
            ans=(1ll*ans*x)%mod;
        x=(1ll*x*x)%mod;
        y=y>>1;
    }
    return ans;
}
void pushup(int id,node a,node b) {
    no[id].valu=1ll*a.valu*b.valu%mod;
    no[id].Status=1ll*a.Status|b.Status;
}
int cur = 1;
void build(int id,int l,int r) {
    no[id].l=l;
    no[id].r=r;
    no[id].lazy=1;
    no[id].Statuslazy=0;
    no[id].Status=0;
    if(l==r) {
        no[id].valu = a[cur] ;
        no[id].Status |= Stauts[cur];
//        cout << Stauts[cur]<< endl;
        cur++;
        return ;
    }
    int mid=(l+r)/2;
    build(id<<1,l,mid);
    build(id<<1|1,mid+1,r);
    pushup(id,no[id<<1],no[id<<1|1]);
}
void pushdown(int id,ll x,ll y) {
    if(no[id].lazy) {
        no[id<<1].lazy=(1ll*no[id<<1].lazy*x)%mod;
        no[id<<1|1].lazy=(1ll*no[id<<1|1].lazy*x)%mod;

        no[id<<1].Statuslazy|=y;
        no[id<<1|1].Statuslazy|=y;
        no[id<<1].Status|=y;
        no[id<<1|1].Status|=y;
        no[id<<1].valu=(1ll*no[id<<1].valu*qpow(x,no[id<<1].r-no[id<<1].l+1))%mod;
        no[id<<1|1].valu=(1ll*no[id<<1|1].valu*qpow(x,no[id<<1|1].r-no[id<<1|1].l+1))%mod;
        no[id].lazy=1;
        no[id].Statuslazy=0;
    }
}
void update(int id,int l,int r,ll x,ll y) {
    if(no[id].l>=l && no[id].r<=r) {
        no[id].valu=(1ll*no[id].valu*qpow(x,no[id].r-no[id].l+1))%mod;
        no[id].Statuslazy|=y;
        no[id].Status|=y;
        no[id].lazy=(1ll*no[id].lazy*x)%mod;
        return ;
    }
    pushdown(id,no[id].lazy,no[id].Statuslazy);
    int mid=(no[id].l+no[id].r)/2;
    if(l<=mid)
        update(id<<1,l,r,x,y);
    if(r>mid)
        update(id<<1|1,l,r,x,y);
    pushup(id,no[id<<1],no[id<<1|1]);
}
node query(int id,int l,int r) {
    if(no[id].l>=l && no[id].r<=r) {
        return no[id];
    }
    pushdown(id,no[id].lazy,no[id].Statuslazy);
    int mid=(no[id].l+no[id].r)/2;
    if(r<=mid)
        return query(id<<1,l,r);
    if(l>mid)
        return query(id<<1|1,l,r);
    node xx,yy;
    xx=query(id<<1,l,r);
    yy=query(id<<1|1,l,r);
    xx.valu=(1ll*xx.valu*yy.valu)%mod;
    xx.Status=(1ll*xx.Status|yy.Status);
    return xx;
}
ll inv[1000020];
void Init() {
    memset(isprime,0,sizeof(isprime));
    isprime[1] = isprime[0] = 1;
    for(int i = 2; i <= 500; i++) {
        if(!isprime[i]) {
            prime[cnt++] = i;
            for(int j = i + i; j <= 500; j += i)
                isprime[j] = 1;
        }
    }
    memset(inv,0,sizeof(inv));
    inv[1]=1;
    for(int i=2; i<1000; i++)
        if(!inv[i])
            for(int j=i; j<1000; j+=i) {
                if(!inv[j])
                    inv[j]=j;
                inv[j]=inv[j]/i*(i-1);
            }
	inv[1]=1;
	for(int i=2;i<=300;i++)
		inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
    for(int i=1;i<cnt;i++)
		f[i]=1ll*inv[prime[i]]*(prime[i]-1)%mod;
    return ;
}

ll getvalue(ll x) {
    ll res = 0;
    for(ll i = 1; prime[i] <= x; i++) {
        if(! (x % prime[i]))
            res |= (1ll << (i - 1ll));
    }
    return res;
}

int main() {
    Init();
    int q;
//    cout << prime[63] << endl;
    while(cin >> n >> q) {
        for(int i = 1; i <= n; i++) cin >> a[i];
        for(int i = 1; i <= n; i++) {
            Stauts[i] = getvalue(a[i]);
        }
        build(1,1,n);
        while(q--) {
            string op;
            int l,r,x;
            cin >> op;
            if(op[0] == 'T') {
                cin >> l >> r;
                node g = query(1,l,r);
                ll stat = g.Status;
//                cout << g.Status <<endl;
                ll ans = g.valu  ;
//                   cout << ans << endl;
                for(int i = 0; i <= 62; i++) {
                    if(stat & (1ll << i)) {
//                        cout << prime[i + 1] << endl;
//                        cout << inv[prime[i + 1]] << endl;
                        ans *= f[i + 1];
//                        cout << f[i + 1] << endl;
                        ans %= mod;
                    }
                }
                cout << ans % mod << endl;
            } else {
                cin >> l >> r >> x;
                ll value = getvalue(x);
                update(1,l,r,x,value);
            }
        }
    }
    return 0;
}
//void update(int id,int l,int r,ll x,ll y)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值