传送门
个人vp补题初体验,若有不当之处多多指正(抱拳)
A 贪心
坑
- 注意可能有星级一样的比赛。星级一样,使用之前星级不一样留下的状态
- 放置的数不超过k
- 最后输出要保持原来顺序:思路一中把要排序的星级数和属性数据用下标分离开
- 二维数组开4e5过大 需要使用动态数组
vector没有分配内存直接下标赋值会卡住 需要push_back
思路
- 要排序的和不变的分离开
- 更新之前的上限值
- 对星级相同的处理:一直使用上一个星级的上限
void solve(){
int n,m,k;
cin>>n>>m>>k;
vector<int>s(n+5),q;
vector< vector<int> >p(n+2,vector<int>(m+5));
forr(i,0,n-1){
q.push_back(i);//q记录下标
cin>>s[i];
forr(j,0,m-1)cin>>p[i][j];
}
//星级排序 下标移动 属性数据顺序不变
sort(q.begin(),q.end(),[&](int x,int y){return s[x]<s[y];});//这里排q使用s的数据 从最小向上递增
int pres=-1,pre=-1,maxn=pre;//pre是上一个的上限 maxn是星级小于本次的总上限 ※需要一直维护上限maxn
for(auto i:q){
// cout<<s[i]<<' ';
//if(s[i]==pres)tot=pre;//错误 因为前一个与其s相等的 pre可能很大 s相等时没有对p的要求
if(s[i]!=pres)pre=maxn;//如果s大于上一个s 就更新上限
int tot=pre+1;//大于之前的上限 tot是至少达到的值
//直接遍历整行
int cur=0;
for(auto &j:p[i]){
if(j!=-1)cur+=j;//遍历加有值的地方
}
//放值
for(auto &j:p[i]){
if(j==-1){
j=(cur>=tot?0:min(k,tot-cur));//注意:放的值[0,k] 如果cur>=tot就是放完了,没值可放
cur+=j;//每次加上放进去的值 0/k/tot-cur
}
}
// cout<<cur<<','<<tot<<endl;
// for(auto j:p[i])cout<<j<<' ';cout<<endl;
if(cur<tot){
cout<<"No"<<endl;//都放k 也达不到tot的要求
return ;
}
maxn=max(maxn,cur);//记录小的部分最大值
pres=s[i];
}
cout<<"Yes"<<endl;
forr(i,0,n-1){
forr(j,0,m-1)cout<<p[i][j]<<' ';
cout<<endl;
}
}
I
思路:字符串s中 一段连续字符串长度l (滑动窗口求长度)
- l为奇数 l-1次操作
- l为偶数 l次操作
移动时l偶数变成奇数优化,能且只能优化掉一次操作
void solve(){
string s;
cin>>s;
int n=s.size();
if(n==1)return cout<<0<<endl,void();
vector<int>l;
int i=0;
for(int j=1;j<=n;j++){
while (s[i]==s[j]&&j<n)j++;
l.push_back(j-i);
i=j;
}
if(i<n)l.push_back(n-i);
if(l.size()==1)return cout<<l[0]/2<<endl,void();
if(s.front()==s.back()){
l.front()+=l.back();
l.pop_back();
}
int opti=0,ans=0;
for(auto &i:l){
ans+=i>>1;
if(i%2==0)opti=1;
}
cout<<ans-opti<<endl;
}
B
人生当中第一个在团队vp中wa的题
- 错解
- 第一次wa
void solve(){
int n,k,ans=0;cin>>n>>k;//竞赛的数量 金牌的比例
int a[N]={0};
forr(i,1,n){
cin>>a[i];
}
int m;
cin>>m;//要分配的队伍数
int tmp=m;
forr(i,1,n){ans+=(a[i]/k);a[i]=k-a[i]%k;}//ans记录分配前的金牌数量 a[i]是增加所需
sort(a+1,a+1+n,cmp);
//forr(i,1,n)cout<<a[i]<<' ';
ans+=m/k;//m还能增加的数量 思路错误
tmp%=k;//m中增加完后剩下的
forr(i,1,n){
if(a[i]<=tmp){
tmp-=a[i];
ans++;
}
}
cout<<ans<<endl;
}
/*应该先满足好满足的
wa例子
3 10
12 11 10
17
应该是5 但是输出了4 wa
- 第二次tle
void solve(){
int n,k,ans=0;cin>>n>>k;
int a[N]={0};
forr(i,1,n){
cin>>a[i];
}
int m;
cin>>m;
int tmp=m;
forr(i,1,n){ans+=(a[i]/k);a[i]=k-a[i]%k;}
sort(a+1,a+1+n,cmp);
//forr(i,1,n)cout<<a[i]<<' ';
int idx=1;
while(tmp){
if(idx>n){idx=1;}//idx多次重复 会tle
if(tmp>=a[idx]){
tmp-=a[idx];
a[idx++]=k;
ans++;
}
if(idx>n){idx=1;}
if(tmp<a[idx])break;
}
cout<<ans<<endl;
}
- 正解
void solve()
{
int n,k;
cin>>n>>k;
LL ans=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
ans+=(a[i]/k);
a[i]=(k-a[i]%k);
}
int m;cin>>m;
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
{
if(m==0) break;
if(m>=a[i])
{
ans++;
m-=a[i];
}
else
{
m=0;
break;
}
}//先往每个位置上放
if(m) ans+=(m/k);
cout<<ans<<endl;
}
G 位操作
异或:如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
1^2 = 3
2^3=1
1^3=2
012^3=0 456^7=0 …
n=1 和 n每4的倍数 连续异或=0 不满足
102 1023(imp) 10243 每到4调换顺序(用queue)
void solve(){
int n;
cin>>n;//0~n-1
if(n==1||n%4==0)return cout<<"impossible"<<endl,void();
queue<int>tmp;
forr(i,0,n-1){
if(i==0||(i+1)%4==0){
tmp.push(i);
}else{
cout<<i<<' ';
if(!tmp.empty()){
cout<<tmp.front()<<' ';tmp.pop();
}
}
}
cout<<endl;
return ;
}
E gcd
- 找最大公因数
辗转相除
int gcd(int a,int b){
return b>0?gcd(b,a%b):a;
}
gcd(a,b)<a也<b
gcd(b,a%b)<a%b
※a%b<a/2;
所以对一个数N最多有logN个不同公约数
- 求一段区间共同的gcd->判断前面的gcd是不是本次数的gcd
- 思路:找lr区间 1~l l~r r~n
1~l:前缀gcd不为1
r~n:后缀gcd不为1
l~r:+k 确定gcd max - 直接枚举lr肯定超时,需要优化
- l是前缀gcd即将要变小的地方
- r从l向后枚举,对lr之间a[i]+k,放进当前前缀gcd(就是g),然后和后缀gcd求,记录最大
void solve(){
int n,k;
cin>>n>>k;
vector<int>a(n+5);
forr(i,1,n){
cin>>a[i];
}
vector<int>pre(n+5);//前缀
vector<int>sub(n+5);//后缀
pre[1]=a[1],sub[n]=a[n];
forr(i,2,n)pre[i]=__gcd(a[i],pre[i-1]);
//forr(i,1,n)cout<<pre[i]<<' ';cout<<endl;
for(int i=n-1;i>=1;i--)sub[i]=__gcd(a[i],sub[i+1]);
int ans=pre[n];//目前最大公因数
forr(i,1,n){
int l,g;
if(pre[i]!=pre[i-1]){//i=1时 pre[0]=0!=pre[1] 会在最开始时遍历一次
l=i,g=pre[i-1];//前缀gcd可能不是一直减小?
forr(r,l,n){//对每个l 枚举r的范围
g=__gcd(g,a[r]+k);
ans=max(__gcd(g,sub[r+1]),ans);
}
}
}
cout<<ans<<endl;
return ;
}