Codeforces Round#687(div2)——解题报告

比赛链接:

https://codeforces.com/contest/1457

A——Prison Break 

题意

给你一个n*m的矩阵以及在矩阵上的一个点,求这个点到这个矩阵四个角的最远曼哈顿距离。

题解

模拟取最大值即可,答案即为max(n-r,r-1)+max(m-c,c-1)。

代码

#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a,ll b){
    return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
    return a/gcd(a,b)*b;
}
inline int read(){
    int s=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
    }
    return s*f;
}
void solve(){
    ll n,m,r,c;scl(n);scl(m);scl(r);scl(c);
    ll res1=max(abs(n-r)+abs(m-c),abs(r-1)+abs(c-1));
    ll res2=max(abs(n-r)+abs(c-1),abs(r-1)+abs(m-c));
    printf("%lld\n",max(res1,res2));
}
int main(){
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE 
#else
    // freopen("in.txt", "r", stdin);
    // debug = 1;
#endif
    time_t beg, end;
    if(debug) beg = clock();

    int T=read();
    while(T--){
        solve();
    }
    if(debug) {
        end = clock();
        printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
    }
    return 0;
}

B——Repainting Street

题意

给你一个长度为n的序列a,a[i]即表示第i位的颜色为a[i],你可以进行一种操作:把[i,i+k-1]位的序列变成一个颜色,让你求出把这个序列变成全一样颜色的最小操作数。

题解

这个题的关键在于颜色数很少,最大为100,因此我们首先暴力枚举序列最后的颜色,然后我们如何最小化操作数,不难想到一个贪心的策略,首先把所有和最终颜色不一样的位置按顺序存入一个vector里面,然后我们遍历vector,不断的执行这两种操作:1.把从最小位置pos开始到pos+k-1位置中颜色不同的变成一个颜色,2.更新最小位置.     中间记录一下答案就可以了。

代码

#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a,ll b){
    return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
    return a/gcd(a,b)*b;
}
inline int read(){
    int s=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
    }
    return s*f;
}
const int N = 1e6+7;
int c[N];
int cnt[107];
void solve(){
    int n=read(),k=read();
    rp(i,1,n) c[i]=read();
    rp(i,1,100) cnt[i]=0;
    rp(i,1,n) cnt[c[i]]++;
    int ans=1e5+7;
    rp(i,1,100){
        if(cnt[i]==0) continue;
        int id=i;
        vector<int> v;v.clear();
        rp(j,1,n) if(c[j]!=id) v.push_back(j);
        if(v.size()==0){
            ans=min(ans,0);
            continue;
        }
        int len=v.size();
        int cur=v[0];
        int res=0;
        int j=0;
        while(j<=len-1){
            while(j+1<=len-1&&v[j+1]-cur+1<=k) j++;
            res++;
            if(j==len-1) break;
            j++;
            cur=v[j];
        }
        ans=min(ans,res);
    }
    printf("%d\n",ans);
}
int main(){
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE 
#else
    freopen("in.txt", "r", stdin);
    debug = 1;
#endif
    time_t beg, end;
    if(debug) beg = clock();

    int T=read();
    while(T--){
        solve();
    }
    if(debug) {
        end = clock();
        printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
    }
    return 0;
}

C——Bouncing Ball

题意

给你一个长度为n的01字符串s,以及一个基点p和周期k,每次取第p位,第p+k位,第p+2k位.....,形成一个新的字符串ss。

你可以进行两种操作:1.把第一位删掉,代价为x(注意ss会变)。2.把ss中的0变成1,代价为y。

让你求出把ss全部变成1的最小代价。

题解

不难发现第一种操作的实际意义就是把p往后挪一位,因此我们可以遍历一遍数组统计一下从p开始周期为k的代价(即0的个数),记录到b[(i-p)%k]。

然后枚举它的基点i在[p,n]范围内,维护一下最小答案(即 偏移多少位的代价 (i-p)*x + 0的个数*y )行了。

代码

#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a,ll b){
    return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
    return a/gcd(a,b)*b;
}
inline int read(){
    int s=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
    }
    return s*f;
}
const int N = 1e5+7;
char s[N];
int cnt[N];
void solve(){
    int n=read(),p=read(),k=read();
    scanf("%s",s+1);
    int x=read(),y=read();
    rp(i,0,k-1) cnt[i]=0;
    rp(i,1,n) if(i>=p) cnt[(i-p)%k]+=(s[i]=='0');
    ll ans=1ll*n*max(x,y);
    rp(j,0,(n-p)/k){
        rp(i,0,k-1){
            if(j*k+i+p>n) break;
            int tt=cnt[i];
            if(s[j*k+i+p]=='0') cnt[i]--;
            ans=min(ans,1ll*tt*x+1ll*y*(j*k+i));
        }
    }
    printf("%lld\n",ans);
}
int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
    freopen("in.txt", "r", stdin);
    debug = 1;
#endif
    time_t beg, end;
    if(debug) beg = clock();
    int T=read();
    while(T--) solve();

    if(debug) {
        end = clock();
        printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
    }
    return 0;
}

D——XOR-gun

题意

给你一个长度为n不严格单调递增的序列a,我们有一种操作:把相邻的两个数a[i]和a[i+1]取出,放进去这两个数异或之后的答案val到第i位,后面往前移。

让你求出把这个序列变成递减序列的最小操作数。

题解

首先需要知道一个结论:任意三个相邻数,如果它们的最高位一样,那么我们可以取最右边相邻两位数异或,异或后的答案肯定比最左边的数小,即符合条件,代价为1。因为数据范围最大为1e9<2^30,且需要保证不严格递增性质,所以当数组长度大于60时,答案肯定为1(考虑极端情况,即1(1),1(1),2(10),2(10),8(100),8(100),16(1000),16(1000)......2^30(1000....00),这个序列最大长度就是60),这样数据范围就很小了,我们可以直接暴力枚举就行了,n^3和n^4的都能过。

代码

#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a,ll b){
	return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
	return a/gcd(a,b)*b;
}
inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
const int N = 1e5+7;
ll a[N];
ll sum[N];
void solve(){
	int n=read();
	sum[0]=0;
	rp(i,1,n){
		scl(a[i]);
		sum[i]=(sum[i-1]^a[i]);
	}
	if(n>60){
		puts("1");
		return ;
	}
	int ans=n+1;
	rp(l,1,n-1){
		rp(r,l+1,n){
			rp(k,r+1,n){
				if((sum[r]^sum[l-1])>(sum[k]^sum[r])){
					ans=min(ans,r-l+k-r-1);
				}
			}
			rp(k,1,l-1){
				if((sum[r]^sum[l-1])<(sum[l-1]^sum[k-1])){
					ans=min(ans,r-l+l-1-k);
				}
			}
		}
	}
	if(ans==n+1) cout<<-1<<endl;
	else cout<<ans<<endl; 
}
int main(){
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
	freopen("in.txt", "r", stdin);
	debug = 1;
#endif
	time_t beg, end;
	if(debug) beg = clock();

	solve();

	if(debug) {
		end = clock();
		printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
	}
	return 0;
}

E——New Game Plus!

题意

(来自https://blog.csdn.net/qq_45458915/article/details/110388504

给出一个长度为 n 的序列a,需要自己定义一种顺序去遍历序列,每次需要维护答案:

  1. ans += sum
  2. sum += a[ i ]

有 k 次机会可以将 sum 清零,问如何遍历可以使得最后的 ans 最大

解法一

首先对于长度为L的序列b,答案就是b[1]*(L-1)+b[2]*(L-2)+......+b[L]*0。对于正数根据贪心的思想不难发现答案就是从大到小后的顺序,

当sum变成负数时,本质还是一样的,只不过因为我们有k次机会可以清0,那么我们就可以把序列分成k+1段子序列(互相独立),类似于高中数学中的挡板思想,然后我们把第一个变成负数的sum和后面的序列放进一个vector里面,从小到大依次放进每一段序列的倒数第一个(b[L]*0),当k+1段的倒数第一个放满后再放倒数第二个,按照这个顺序模拟到所有序列放完,最后对每一段序列求一下答案,再累加一下就行了。

代码

#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a,ll b){
    return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
    return a/gcd(a,b)*b;
}
inline int read(){
    int s=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
    }
    return s*f;
}
const int N = 1e5+7;
void solve(){
    int n=read(),k=read();
    vector<int> v1,v2;
    rp(i,1,n){
        int x=read();
        if(x>=0) v1.push_back(x);
        else v2.push_back(x);
    }
    sort(v1.begin(),v1.end(),greater<int>());
    ll cur=0,ans=0;
    for(auto val:v1){
        ans+=cur;
        cur+=val;
    }
    sort(v2.begin(),v2.end(),greater<int>());
    vector<int> v;int len=v2.size();
    rp(i,0,len-1){
        if(cur<0){
            v.push_back(cur);
            while(i<=len-1){
                v.push_back(v2[i]);
                i++;
            }
        }
        else{
            ans+=cur;
            cur+=v2[i];
        }
    }
    // for(auto val:v) cout<<val<<" ";
    // cout<<endl;
    k++;
    sort(v.begin(),v.end());
    len=v.size();
    rp(i,0,len-1) ans+=1ll*v[i]*(i/k);
    cout<<ans<<endl;
}
int main(){
    //ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
    freopen("in.txt", "r", stdin);
    debug = 1;
#endif
    time_t beg, end;
    if(debug) beg = clock();

    solve();

    if(debug) {
        end = clock();
        printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
    }
    return 0;
}

解法二

比较难想的贪心,根据解法一,最终我们是要把序列分成k+1段的,因此我们把数从大到小排序后,每次都把当前数加到和最大的那个组就行了。

代码

#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a,ll b){
	return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
	return a/gcd(a,b)*b;
}
inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=s*10+ch-'0';
		ch=getchar();
	}
	return s*f;
}
const int N = 5e5+7;
int a[N];
void solve(){
	int n=read(),k=read();
	rp(i,1,n) a[i]=read();
	sort(a+1,a+n+1,greater<ll>());
	priority_queue<ll> q;
	rp(i,1,k+1) q.push(0ll);
	ll ans=0;
	rp(i,1,n){
		ans+=q.top();
		// cout<<q.top()<<endl;
		ll tmp=q.top()+a[i];
		q.pop();
		q.push(tmp);
	}
	cout<<ans<<endl;
}
int main(){
	//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
	freopen("in.txt", "r", stdin);
	debug = 1;
#endif
	time_t beg, end;
	if(debug) beg = clock();

	solve();

	if(debug) {
		end = clock();
		printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
	}
	return 0;
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值