Music Problem
思路:
常规思路:
模数取
3600
3600
3600,很容易想到
T
∗
n
∗
3600
T*n*3600
T∗n∗3600 的做法,空间上优化第一维,但时间复杂度太大
数学优化:
题解有人讲到抽屉原理:一个由
n
n
n 个数组成的数列 一定能找出若干个连续的数使它们之和能被
n
n
n 整除。特判掉
n
>
=
3600
n>=3600
n>=3600 的情况,常规思路就可以通过了,我直呼
N
B
NB
NB,不愧是清华的大佬
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 1e5 + 9;
const int mod = 3600;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
int f[2][4000];
void work()
{
cin >> n;
if(n >= 3600){
for(int i = 1, x; i <= n; ++i) cin >> x;// 这里要读完再输出,不然会wa
cout << "YES" << endl;return;
}
for(int i = 0; i <= mod; ++i)
for(int j = 0; j < 2; ++j)
f[j][i] = 0;
int op = 1;
for(int i = 1, x; i <= n; ++i, op ^= 1){
cin >> x;
x %= mod;
f[op][x] = 1;
for(int j = mod - 1; j >= 0; --j){
f[op][j] = max(f[op][j], f[op^1][j]);
int k = (x + j) % mod;
f[op][k] = max(f[op][k], f[op^1][j]);
}
}
cout << (f[op^1][0] ? "YES" : "NO") << endl;
}
int main()
{
ios::sync_with_stdio(0);
int TT;cin>>TT;while(TT--)
work();
return 0;
}
二进制优化思路:
把
a
i
%
m
o
d
a_i \ \% \ mod
ai % mod 分成
m
o
d
mod
mod 组,然后一组有
c
i
c_i
ci 个数,然后类似多组背包二进制优化的方法,可以很快的通过,很好用的优化思路,可以直接莽这个方法
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 1e5 + 9;
const int mod = 3600;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
int f[2][4000];
int cnt[4000];
void work()
{
cin >> n;
for(int i = 0; i < mod; ++i)
for(int j = 0; j < 2; ++j)
f[j][i] = 0;
for(int i = 0; i < mod; ++i) cnt[i] = 0;
for(int i = 1, x; i <= n; ++i) cin >> x, cnt[x%mod]++;
if(cnt[0]){// 速度大幅提升主要是因为这个
cout << "YES\n";return;
}
int op = 1;
for(int i = 1; i < mod; ++i){
if(!cnt[i]) continue;
vector <int> v;
int c = cnt[i];
for(int j = 1; j <= c; j <<= 1)
{
v.push_back(i * j % mod);
c -= j;
}
if(c) v.push_back(i * c % mod);
for(auto d : v)
{
f[op][d] = 1;
for(int j = mod - 1; j >= 0; --j)
{
f[op][j] = max(f[op][j], f[op^1][j]);
int k = (d + j) % mod;
f[op][k] = max(f[op][k], f[op^1][j]);
}
op ^= 1;
}
}
cout << (f[op^1][0] ? "YES" : "NO") << endl;
}
int main()
{
ios::sync_with_stdio(0);
int TT;cin>>TT;while(TT--)
work();
return 0;
}
betset优化:
用
b
i
t
s
e
t
bitset
bitset 实现这道题的神奇做法
代码简洁且高效,就是稍微有点抽象
S
T
L
STL
STL
N
B
NB
NB
code:
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 4e3 + 9;
const int mod = 3600;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
void work()
{
cin >> n;
bitset <maxn * 2> f;
for(int i = 1, x; i <= n; ++i){
cin >> x; x %= mod;
f |= f << x | f >> (mod - x);
f[x] = 1;
}
cout << (f[0] ? "YES" : "NO") << endl;
}
int main()
{
ios::sync_with_stdio(0);
int TT;cin>>TT;while(TT--)
work();
return 0;
}