23.11.5周报

30日每日三题A题:

题意:一字符串s根据字符串w来构造,构造条件见图中三个if,现给出x和构造好的s,要求还原w。

思路:没怎么碰到过的构造题,s[i]为1有很多种情况,但s[i]为0则对应的w必为0,将w初始化为1(这里用memset函数可能会出问题,可以用for循环),然后将对应的w变为0,再构造一遍s和给出的s作对比。

代码如下:

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using PII=pair<int,int>;

const int N=1e5+10;
int n;
string s;
int x;
int w[N],ss[N];
void solve(){
	cin >> s >> x;
	n=s.size();
	for(int i=1;i<=n;i++) w[i]=1;
	s=" "+s;
	for(int i=1;i<=n;i++){
		if(i<=x && i+x<=n){
			if(s[i]=='0') w[i+x]=0;
		}
		if(i+x>n && i-x>0){
			if(s[i]=='0') w[i-x]=0;
		}
		if(i>x && i+x<=n){
			if(s[i]=='0') w[i-x]=0,w[i+x]=0;
		}
	}
	for(int i=1;i<=n;i++){
		if(i<=x && w[i+x]==0) ss[i]='0';
		else if(i+x>n && w[i-x]==0) ss[i]='0';
		else if(i>x && i+x<=n && w[i-x]==0 && w[i+x]==0) ss[i]='0';
		else ss[i]='1';
	}
	for(int i=1;i<=n;i++)
		if(s[i]!=ss[i]){cout << -1 << endl; return ;}
	for(int i=1;i<=n;i++) cout << w[i];cout << endl;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	cin >> t;
	while(t--) solve();
	return 0;
}
//make it count
//开ll plz

B题:

题意:给一全为0的数组,经过m次操作使其平均值最大,每次操作输入x和d,选一下标i使每个元素aj(j从1到n)+=x+d*dis(i,j),dis(i,j)为其下标差,问能达到最大平均值为多少,输出(注意精度1e-6)

思路:贪心,d为正i取1,d为负i取中间。

星期二比赛做了道dfs

全排列的写法,过了样例但wa,有点郁闷,先把代码贴这:

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using PII=pair<int,int>;

const int N=1e6+10;
struct nod{
	ll a,b,c,x,y;
}pm[19];
ll n,t,p;
ll ans,sum;
bool vi[19];
void dfs(int x,ll tim){
	if(tim>t) return ;
	sum+=max((ll)pm[x].c,pm[x].a-tim*pm[x].b-pm[x].y*p);
	ans=max(sum,ans);
	for(int i=1;i<=n;i++){
		if(!vi[i]){
			vi[i]=1;
			dfs(i,tim+pm[i].x);
			vi[i]=0,sum=0;
		}	
	}
}
void solve(){
	cin >> n >> t >> p;
	for(int i=1;i<=n;i++){
		cin >> pm[i].a >> pm[i].b >> pm[i].c >> pm[i].x >> pm[i].y;
	}
	dfs(0,0);
	cout << ans;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin >> t;
	while(t--) solve();
	return 0;
}
//make it count

周三每日三题A题:

题意:一字符串左循环转换为将头字符放尾部,右循环转换为将尾部字符放头部,若左转和右转结果相同则是好字符串。给一字符串s,问至少删去多少字符使其变好。

思路:好字符串有两种情况,一是其中字符全相同,二是abab式排列(易得),问题在于代码怎么实现,然后发现是暴力,算了下时间,最多可以到2e10,就感觉很离谱。也算一个经验,就以后做题先把暴力枚举复杂度算下,想不出来就暴力,说不定就过了。

代码如下:

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using PII=pair<int,int>;

const int N=2e5+10;
int n,m;
string s;
int cnt[11];
int ans;
void solve(){
	memset(cnt,0,sizeof(cnt));
	cin >> s;
	n=s.size();
	s=" "+s;
	for(int i=1;i<=n;i++){
		cnt[s[i]-'0']++;
	}
	sort(cnt,cnt+10,greater<int>());
	ans=n-cnt[0];//第一种情况
	for(int i=0;i<=9;i++){
		for(int j=0;j<=9;j++){
			if(i==j) continue;
			int flag=0;
			for(int k=1;k<=n;k++){
				if(!(flag&1) && s[k]-'0'==i){flag++;}
				if(flag&1 && s[k]-'0'==j){flag++;}
			}
			ans=min(n-flag/2*2,ans);//第二种情况
		}
	}
	cout << ans << endl;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	cin >> t;
	while(t--) solve();
	return 0;
}
//make it count
//开ll plz

星期四每日三题:

A题:

题意:n个摊位围成圈,每个摊位花费ai元,有tt元,到一摊位时,能买就买一次,不能买就不买,然后继续往下个摊位走,直至再不能买到东西(tt<最小的ai),问能买多少次东西

思路:最开始想的是用前缀和二分模拟,每次二分找出能买的区间,直至一轮后购买数为0退出循环,但tle了,不过就当练练二分也可以,还是把代码贴出来,如下:

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using PII=pair<int,int>;

const int N=2e5+10;
int n;
ll a[N],sum;
ll tt;
ll ans;
int erfen(int x){
	if(tt<a[x]-a[x-1]) return 0;
	int l=x,r=n,ans=0;
	while(l<=r){
		int mid=l+(r-l)/2;
		a[mid]-a[x-1]<=tt?ans=mid,l=mid+1:r=mid-1;
	}
	return ans;
}
void solve(){
	cin >> n >> tt;
	for(int i=1;i<=n;i++) cin >> a[i],sum+=a[i],a[i]+=a[i-1];
	ans+=tt/sum*n;
	tt%=sum;
	while(tt>0){
		int buy=0,i=1;
		while(i<=n){
			int j=erfen(i);
			if(j==0) i++;
			else tt-=a[j]-a[i-1],buy+=j-i+1,i=j+1;
		}
		if(buy) ans+=buy;
		else break;
	}
	cout << ans;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin >> t;
	while(t--) solve();
	return 0;
}
//make it count
//开ll plz

然而这道题其实并不需要前缀二分什么的,只是每轮都要用模运算。

代码如下:

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using PII=pair<int,int>;

const int N=2e5+10;
int n;
ll a[N],sum;
ll tt;
ll ans,cnt;
void solve(){
	cin >> n >> tt;
	for(int i=1;i<=n;i++){
		cin >> a[i];
		if(tt-sum>=a[i]){
			sum+=a[i];
			cnt++;
		}
	}	
	while(sum>0 && tt>0){
		ans+=tt/sum*cnt;
		tt%=sum;
		sum=0,cnt=0;
		for(int i=1;i<=n;i++){
			if(tt-sum>=a[i]){
				sum+=a[i];
				cnt++;
			}
		}
	}
	cout << ans;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin >> t;
	while(t--) solve();
	return 0;
}
//make it count
//开ll plz

周六每日三题:

A题:

题意:给一字符串,udlr代表每步的方向,初始位置(0,0),目标位置(x,y),问最小修改区间长度使得能走到x,y(区间内可任意修改),如无法到达输出-1

思路:一道二分滑动窗口,不过还得用上前缀和,和k dominan character有一点点像,但不是很像。

代码如下:

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using PII=pair<int,int>;

const int N=2e5+10;
int n,m;
string s;
int fx,fy;
int xx[N],yy[N];
int ans=-1;
bool check(int x){
	int l=1,r=x;
	while(r<=n){
		int nx=xx[n]-xx[r]+xx[l-1],ny=yy[n]-yy[r]+yy[l-1];
		int ab=abs(nx-fx)+abs(ny-fy);
		if(x>=ab && !((x-ab)&1)) return 1;
		l++,r++;
	}
	return 0;
}
void solve(){
	cin >> n >> s >> fx >> fy;
	if(n<abs(fx)+abs(fy)){cout << -1; return ;}
	for(int i=0;i<n;i++){
		xx[i+1]=xx[i],yy[i+1]=yy[i];
		if(s[i]=='U') yy[i+1]++;
		if(s[i]=='D') yy[i+1]--;
		if(s[i]=='L') xx[i+1]--;
		if(s[i]=='R') xx[i+1]++;
	}
	int l=0,r=n;
	while(l<=r){
		int mid=l+(r-l)/2;
		check(mid)?ans=mid,r=mid-1:l=mid+1;
	}
	cout << (ans==-1?-1:ans);
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin >> t;
	while(t--) solve();
	return 0;
}
//make it count
//开ll plz

说到前缀和,就顺便去网上逮了道前缀和来做:

题意:你有本死亡笔记,1到n天每天写ai个名字,每页写满m个名字要翻页,问每天要翻多少页(每天单独计算)

思路:如果不是已经知道了可以用前缀和,我大概会当作模拟来做,但如果用前缀和的话,代码就会又短又快。如下:

#include <bits/stdc++.h>
using namespace std;
using ll=long long;
using PII=pair<int,int>;

const int N=2e5+10;
int n,m;
ll a[N];

void solve(){
	cin >> n >> m;
	for(int i=1;i<=n;i++) cin >> a[i],a[i]+=a[i-1];
	for(int i=1;i<=n;i++) a[i]/=m,cout << a[i]-a[i-1] << " ";
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t=1;
	//cin >> t;
	while(t--) solve();
	return 0;
}
//make it count
//开ll plz

星期天打了个比赛。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值