比赛链接:https://codeforces.com/contest/1323/
日常记录自己的菜鸡行径
虽然我打的是div1,但是我深刻认识到我水平其实只有div2的(确信),不然我也不会狂掉101分
A. Even Subset Sum Problem
签到题,略
#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
typedef long long ll; ll read(){ll x; if(scanf("%lld",&x)==-1)exit(0); return x;}
const int N=1000010;
vector<int> a[2];
signed main(){
ios_base::sync_with_stdio(0); cin.tie(0);
int t=read();
while(t--){
a[0].clear();
a[1].clear();
int n=read();
repeat(i,1,n+1){
int x=read()%2;
a[x].push_back(i);
}
if(!a[0].empty())cout<<1<<endl<<a[0][0]<<endl;
else if(a[1].size()>1)cout<<2<<endl<<a[1][0]<<' '<<a[1][1]<<endl;
else cout<<-1<<endl;
}
return 0;
}
B. Count Subrectangles
(这题居然在B题位置)
首先要统计一下序列 \(a\) 和 \(b\) 的所有连续 \(1\) 区间长度,记 \(A_i\) 为 \(a\) 序列第 \(i\) 个连续 \(1\) 的区间长度,\(B_i\) 类似
考虑 \(k\) 的一个约数 \(d\),那么 \(d\times\dfrac k d\) 矩形的个数就是大于 \(0\) 的 \((A_i-d+1)\) 之和乘以大于 \(0\) 的 \((B_i-\dfrac k d+1)\) 之和,记这个数为 \(cnt(d)\)
最终答案就是所有的 \(cnt(d)\) 加起来,即 \(\sum_{d|k}cnt(d)\)
加速的话可以将 \(A_i\) 和 \(B_i\) 排个序后求前缀和,用上 lower_bound
总时间复杂度大概 \(O(\sqrt{nm}\log(nm))\)
更:根本不用前缀和,我发现去掉前缀和复杂度没什么变化,所以只要 lower_bound
就行了
再更:连 lower_bound
都不用也过了,虽然我有点没看懂
#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
typedef long long ll; ll read(){ll x; if(scanf("%lld",&x)==-1)exit(0); return x;}
const int N=40010;
vector<int> divisor;
int a[N],b[N],a_sum[N],b_sum[N]; //这个a,b实际上是题解中的A,B
void get_divisor(int n){ //求出所有约数,这是我研究出来(大雾)的数论分块求法
for(int i=1;i<n;i=n/(n/(i+1)))
if(n%i==0)
divisor.push_back(i);
divisor.push_back(n);
}
int input(int a[],int a_sum[],int n){ //读入、排序、计算前缀和
int len=0,cnt=0;
a[cnt++]=0;
repeat(i,0,n){
if(read())len++;
else if(cnt>0)a[cnt++]=len,len=0;
}
if(len>0)a[cnt++]=len;
sort(a,a+cnt);
repeat(i,1,cnt)a_sum[i]=a_sum[i-1]+a[i];
return cnt;
}
ll count(int a[],int a_sum[],int n,int d){ //计算所有大于0的(ai-d+1)之和
int p=lower_bound(a,a+n,d)-a;
ll ans=a_sum[n-1]-a_sum[p-1]-(n-p)*(d-1);
return ans;
}
signed main(){
ios_base::sync_with_stdio(0); cin.tie(0);
int n=read(),m=read();
ll k=read(); get_divisor(k);
n=input(a,a_sum,n); m=input(b,b_sum,m);
ll ans=0;
for(auto d:divisor){
ans+=count(a,a_sum,n,d)*count(b,b_sum,m,k/d);
}
cout<<ans<<endl;
return 0;
}
C. Unusual Competitions
div1签到题
括号序列的一种常见处理方式是把 '('
看作 \(1\) ,把 ')'
看作 \(-1\),然后求前缀和
这时候前缀和小于 \(0\) 的区间(再加上区间之后的那个位置)就是需要重排的区间,把区间长度 \(+1\) 计入答案中
比如样例中的 ))((())(
有:
) | ) | ( | ( | ( | ) | ) | ( | |
---|---|---|---|---|---|---|---|---|
括号对应的值 | -1 | -1 | 1 | 1 | 1 | -1 | -1 | 1 |
前缀和 | -1 | -2 | -1 | 0 | 1 | 0 | -1 | 0 |
是否要重排 | √ | √ | √ | √ | × | × | √ | √ |
(如果前缀和最后一个位置不是0直接输出-1就行了)
#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
const int N=1000010;
string s;
int H[N],*h=H+1,n,ans=0;
signed main(){
ios_base::sync_with_stdio(0); cin.tie(0);
cin>>n>>s;
repeat(i,0,n){
h[i]=h[i-1]+(s[i]=='(')*2-1;
}
if(h[n-1]!=0)cout<<-1<<endl,exit(0);
repeat(i,0,n)ans+=(h[i]<0)+(h[i]<0 && h[i-1]>=0);
cout<<ans<<endl;
return 0;
}
D. Present
div1的第二题,我成功被难住
更:只能说这种题目做太少了,题解思路很牛逼,菜是原罪
我们分析答案的每一位,如果答案的第 \(i\) 位是 \(1\),这意味着有奇数个 \((j,k)\) 满足 \(a_j+a_k\) 的第 \(i\) 位是 \(1\)
首先我们要砍掉高于 \(i\) 位的信息(这对答案不产生影响),即让 b[j]=a[j]%(1<<(i+1))
(好处是这些数字两两之和被限制得很小),于是第 \(i\) 位是 \(1\) 的 \(b_j+b_k\) 的值只能在区间 \([2^i,2^{i+1}-1]∪[2^{i+1}+2^i,2^{i+2}-1]\) 中
这时如果固定 \(j\) 的值,\(b_k\) 的值的区间就是 \([2^i-b_j,2^{i+1}-1-b_j]∪[2^{i+1}+2^i-b_j,2^{i+2}-1-b_j]\),因此能在排序后用 lower_bound
快速计算满足 \(b_j+b_k\) 的第 \(i\) 位是 \(1\) 的 \(k\) 的个数
复杂度是 \(O(n\log n\log C)\)
题解里留了个问题:Can you do it in \(O(n\log C)\)?
不会,告辞!(有思路的可以告诉我orz)
说明一下其他题解里的 \([...]∪[...,2^{i+2}-2]\) 和 \([...]∪[...,2^{i+2}-1]\) 是没区别的,因为最大只能是 \(2^{i+2}-2\)
//代码大部分复制于%%%lzj
#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
typedef long long ll; ll read(){ll x; if(scanf("%lld",&x)==-1)exit(0); return x;}
const int N=400010;
int a[N],b[N];
signed main(){
int n=read();
repeat(i,0,n)a[i]=read();
int ans=0;
repeat(i,0,26){ //答案一定小于1<<26
int mod=1<<(i+1);
repeat(j,0,n)b[j]=a[j]%mod;
int s=0;
sort(b,b+n); //排序方便二分查找
repeat(j,0,n){
int l,r;
l=lower_bound(b,b+n,(1<<i)-b[j])-b;
r=lower_bound(b,b+n,(1<<(i+1))-b[j])-b-1;
s+=(r-l+1); //统计2^i到2^(i+1)-1区间内的解
l=lower_bound(b,b+n,(1<<i)+(1<<(i+1))-b[j])-b;
r=lower_bound(b,b+n,(1<<(i+2))-b[j])-b-1;
s+=(r-l+1); //统计2^(i+1)+2^i到2^(i+2)-1区间内的解
if((b[j]+b[j])&(1<<i))s--; //自己和自己相加的不算
}
if((s/2)&1)ans+=1<<i; //小于j和大于j的都记录了所以个数要除以2
}
cout<<ans<<endl;
return 0;
}