牛客 Music Problem
题意:
给一个长度为n的数组a,问是否存在一个子序列,满足该子序列的和模3600等于0
数据范围:n<=1e5,a(i)<=1e9
解法:
比较裸的题,但是O(3600n)的算法不够优,
利用用bitset的整体移位,可以非常快的更新每轮答案。
code:
#include<bits/stdc++.h>
using namespace std;
signed main(){
int T;cin>>T;
while(T--){
bitset<3600>t;
int n;cin>>n;
for(int i=1;i<=n;i++){
int x;cin>>x;
x%=3600;
t|=(t<<x)|(t>>(3600-x));
t[x]=1;
}
if(t[0])puts("YES");
else puts("NO");
}
return 0;
}
hdu5890 Eighty seven
题意:
给定n个数,q组询问,每组询问给出x,y,z,表示删掉编号为x,y,z的数
问能否从剩下的数中选出10个数,满足这十个数的和为87
注意x,y,z中可能有部分数相等
数据范围:n<=50,q<=1e5
解法:
观察到q非常大,对于每组询问都计算一遍显然是不行的。
考虑到n很小,可以直接预处理所有情况,对于每组询问O(1)输出
预处理的过程用bitset优化
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=55;
int f[maxm][maxm][maxm];
bitset<88>d[11];//d[i]表示选择i个数可以达到的状态
int a[maxm];
int n;
bool check(int x,int y,int z){
for(int i=1;i<=10;i++)d[i].reset();//清空
d[0][0]=1;
for(int i=1;i<=n;i++){
if(i==x||i==y||i==z||a[i]>87)continue;
for(int j=10;j>=1;j--){
d[j]|=(d[j-1]<<a[i]);
}
}
return d[10][87];
}
signed main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
memset(f,0,sizeof f);
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
for(int k=j;k<=n;k++){
if(check(i,j,k)){
f[i][j][k]=f[i][k][j]=1;
f[j][i][k]=f[j][k][i]=1;
f[k][i][j]=f[k][j][i]=1;
}
}
}
}
int q;scanf("%d",&q);
while(q--){
int x,y,z;scanf("%d%d%d",&x,&y,&z);
if(f[x][y][z])puts("Yes");
else puts("No");
}
}
return 0;
}
牛客 简单瞎搞题
题意:
数据范围:n,l,r<=100
解法:
x(i)的可能取值为l2,(l+1)2…r2
注意到数据范围只有100,那么总和最大1e6,
开一个1e6的bitset,对于每一个x枚举其可能的取值,统计哪些值可以达到即可
code:
#include<bits/stdc++.h>
using namespace std;
bitset<1000000+5>t[105];
signed main(){
int n;scanf("%d",&n);
t[0][0]=1;
for(int i=1;i<=n;i++){
int l,r;scanf("%d%d",&l,&r);
for(int j=l;j<=r;j++){
t[i]|=(t[i-1]<<(j*j));
}
}
printf("%d\n",t[n].count());
return 0;
}
poj2443 Set Operation
题意:
有n个集合,每个集合S(i)有C(i)个数
q组询问,每组询问给出两个数x,y
问是否存在一个k,满足x在集合S(k)种,y也在集合S(k)中
数据范围:n<=1e3,C(i)<=1e4,1<=每个数<=1e4,q<=2e5
解法:
因为数的范围为1e4,集合数为1e3,
可以开1e4个bitset,每个bitset的大小为1e3,设第i个bitset为t(i)
如果集合k中出现了x,那么标记t(x,k)为1
判断x,y是否同时存在于一个集合,只需要判断t(x)&t(y)是否有含1的位即可
code:
#include<cstdio>
#include<bitset>
using namespace std;
bitset<1005>t[10000+5];//t[i][j]表示值i在第j个集合是否出现过
signed main(){
int n;scanf("%d",&n);
for(int i=1;i<=n;i++){
int k;scanf("%d",&k);
while(k--){
int x;scanf("%d",&x);
t[x][i]=1;
}
}
int q;scanf("%d",&q);
while(q--){
int x,y;scanf("%d%d",&x,&y);
if((t[x]&t[y]).any())puts("Yes");
else puts("No");
}
return 0;
}