这场是英文题面QAQ,dlstql
每个数字有一个状态表示它在各个集合的情况:0表示不在任何集合,1表示在第一个集合,3表示在第一个和第二个集合……
显然,状态为0的数字可以不用管,状态为 2 k − 1 2^k-1 2k−1的数字必定单独一个占一个集合。
那么当k=2,状态为1的和为2的可以彼此配对,构成 m a x ( n u m [ 1 ] , n u m [ 2 ] ) max(num[1],num[2]) max(num[1],num[2])个集合(num[state]表示状态为state的数字个数)。
当k=3的时候,二进制中有两个1的状态只能和另一种和它互补的状态合并,而且合并掉肯定比不合并要优,所以贪心的合并之后取 m a x ( n u m [ 1 ] , n u m [ 2 ] , n u m [ 4 ] ) max(num[1], num[2],num[4]) max(num[1],num[2],num[4])就可以了。
#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
int n, k;
const int maxn = 1e5 + 50;
int state[maxn];
int num[8];
int t[3];
int main()
{
cin>>n>>k;
for(int i = 0; i < k; ++i){
cin>>t[i];
for(int j = 0; j < t[i]; ++j){
int x; scanf("%d", &x); state[x] |= (1<
注意到数据范围很小, O ( N 3 ) O(N^3) O(N3)枚举前3个数字然后直接判断第四个数字就可。
#include
using namespace std;
const int maxn = 330;
int cnt[maxn][maxn];
int a[maxn], b[10];
int n;
void read_data(){
scanf("%d",&n);
memset(cnt,0,sizeof(cnt));
for (int i=1;i<=n;++i){
for (int j=1;j<=n;++j){
cnt[i][j]=cnt[i-1][j];
}
scanf("%d",&a[i]);
cnt[i][a[i]]++;
}
for (int i=1;i<=4;++i){
scanf("%d",&b[i]);
}
return ;
}
int calc(int pos, int x1, int x2, int x3){
int res=pos;
res-=cnt[pos][x1];
if (x2!=x1){
res-=cnt[pos][x2];
}
if ((x3!=x1)&&(x3!=x2)){
res-=cnt[pos][x3];
}
return res;
}
void sol(){
long long ans=0;
long long lcnt=0;
for(int i=n;i>=1;--i){
for(int j=i-1;j>=1;--j){
if ((b[4]==b[3])^(a[i]==a[j])){
continue;
}
for (int k=j-1;k>=1;--k){
if ((b[3]==b[2])^(a[j]==a[k])){
continue;
}
if ((b[4]==b[2])^(a[i]==a[k])){
continue;
}
lcnt++;
if (b[1]==b[2]){
ans+=cnt[k-1][a[k]];
continue;
}
if (b[1]==b[3]){
ans+=cnt[k-1][a[j]];
continue;
}
if (b[1]==b[4]){
ans+=cnt[k-1][a[i]];
continue;
}
ans+=calc(k-1,a[i],a[j],a[k]);
}
}
}
cout<
可以双向枚举 i i i:
从2开始枚举 i i i获取 i i i的逆元 j j j,那么逆元为i的数字j也就得到了,维护一个枚举上限 u u u,当 j j j小于 u u u的时候就获得了一个答案并更新 u u u。因为逆元为 2 , 3 , . . j 2,3,..j 2,3,..j的数字位置都已经在 u u u后面获得,所以u后面必然不会再有新答案产生。枚举到 i i i大于等于 u u u的时候终止就可以了。
#include
using namespace std;
const int maxn = 1e6+10;
long long _inv[maxn];
void sol(){
long long P,MXINV,inv;
scanf("%lld",&P);
MXINV=P;
stack > limit;
queue > ans;
for (long long i=2;i=limit.top().first){
while(!limit.empty()){
ans.push(limit.top()); limit.pop();
}
break;
}
}
_inv[i]=((P-P/i)*_inv[P%i])%P;
inv=_inv[i];
if (invinv){
limit.push(make_pair(inv,i));
}
}
}
}
printf("%d\n",ans.size());
while(!ans.empty()){
printf("%lld %lld\n",ans.front().first,ans.front().second); ans.pop();
}
return ;
}
int main(){
_inv[0]=_inv[1]=1;
int T;
scanf("%d",&T);
while(T--){
sol();
}
return 0;
}
先差分获得操作后的矩形,然后用4叉树或者二维线段树查询。
注意4叉树要加剪枝,查询到结点最大值小于等于答案就跳出。当然也可以对线段树加剪枝。
四叉树版本:
9600ms
#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 2e3+5;
ll a[maxn][maxn];
int n, m1, m2;
ll val[maxn*maxn*8];
void build(int rt, int x1, int y1, int x2, int y2){
//cout<<"rt:"<
二维线段树版本
1991ms
#include
#define ll long long
#define mid ((l+r)>>1)
using namespace std;
const int maxn = 2005;
ll a[maxn][maxn];
ll val[maxn<<2][maxn<<2];
int n;
inline int id(int l, int r){
return (l + r | l != r);
}
void build_y(int L, int l, int r){
int rt = id(l, r);
int pos = id(L, L);
if(l == r){
val[pos][rt] = a[L][l];
//cout<<"i:"< mid) ans = max(ans, qry_y(pos, mid+1, r));
return ans;
}
ll qry_x(int l, int r){
if(lx <= l && r <= rx){
return qry_y(id(l, r), 1, n);
}
ll ans = 0;
if(lx <= mid) ans = max(ans, qry_x(l, mid));
if(rx > mid) ans = max(ans, qry_x(mid+1, r));
return ans;
}
int m1, m2;
int main()
{
cin>>n>>m1>>m2;
while(m1--){
int x1, x2, y1, y2; ll w;
scanf("%d%d%d%d%lld", &x1, &y1, &x2, &y2, &w);
a[x1][y1] += w;
a[x1][y2+1] -= w;
a[x2+1][y1] -= w;
a[x2+1][y2+1] += w;
}
for(int i = 1; i <= n; ++i)
for(int j = 1; j<= n; ++j)
a[i][j] += a[i-1][j] + a[i][j-1] - a[i-1][j-1];
build_x(1, n);
while(m2--){
scanf("%d%d%d%d", &lx, &ly, &rx, &ry);
printf("%lld\n", qry_x(1, n));
}
}
可以把 2 k ∗ 2 k 2^k*2^k 2k∗2k个导致的结果都弄出来,最多是1024个1024位的向量,然后求这些01向量任意异或能组合成多少个不同的向量,求一下线性基的个数 r r r, 2 r 2^r 2r就是答案
#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define ull unsigned long long
using namespace std;
const int maxn = 1050;
bitset p[1050];
ull sed = 131;
int a[maxn][maxn];
char s[maxn][maxn];
int n, k;
void sol(int x, int y){
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j){
if(s[i][j] == '1'){
a[(x+i)%n][(y+j)%n] = 1;
}
else a[(x+i)%n][(y+j)%n] = 0;
}
}
bitset v; v.reset();
for(int i = 0; i < n; ++i){
for(int j = 0; j < n; ++j){
v[i*n+j] = a[i][j];
}
}
for(int i = 0; i < n*n; ++i){
if(v[i] && p[i].count()){
v = v^p[i];
}else if(v[i]){
p[i] = v;
break;
}
}
}
const ll mod = 1e9 + 7;
ll qm(ll a, ll b){
ll res = 1;
while(b){
if(b&1) res = res*a%mod;
a = a*a%mod;
b >>= 1;
} return res;
}
int main()
{
cin>>k; n = 1<