T1:
题意:求序列中两两取& | ^的最大值。。
这道题相当于三道题。。
XOR就是Trie树上贪心即可,注意一开始不可以加入0这个串,并且插入a[1]后从i=2开始匹配。
另外,匹配的时候两种写法。
第一种取反后匹配,这里要注意取反后不能限制最高位!!因为有可能超出最高位的位置可以填1!!
第二种的话,就是一边匹配一边用ans记录,或者说还是用单词节点,只不过匹配的时候要每位取反。。
AND操作首先应让最高位为1,那么只有一个数在这个二进制位上是1的话,那么这一位就应该填入0,并且去掉那个1 ,如果有两个以上,那么这位一定为1,再删掉所有该位不为1的数,注意这里同时要删掉低位为1的个数。
OR操作,对于一个数a[i],其二进制的所有子集所对应的数都可以用a[i]来填补(因为是|),那么首先把所有可以用a数组中某个数填补的数用数组标记,然后枚举每一个a[i],以及第二个数的每一位,判断加上这一位之后能不能被填补,最后得到的就是最大值。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<list>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 100005;
const int maxbit = 23;
int a[maxn];
int n;
struct Trie
{
int son[maxn<<5][2];
int key[maxn<<5];
int maxnode;
#define son(x,d) son[x][d]
#define key(x) key[x]
void clear() { memset(son,0,sizeof(son)); memset(key,0,sizeof(key)); maxnode=0; }
void insert(int val)
{
int root=0;
for(int k=21;k>=1;k--)
{
int d = (val&(1<<k-1))>>k-1;
if(!son(root,d)) son(root,d)=++maxnode;
root = son(root,d);
}
key(root) = val;
}
int query(int x)
{
/*int tmp = x , highest = 0;
while(tmp) highest++,tmp>>=1;
int to = (~x)&((1<<highest)-1);*/
int to = (~x);
int root = 0;
for(int k=21;k>=1;k--)
{
int d = (to&(1<<k-1))>>k-1;
if(son(root,d)) root = son(root,d);
else root = son(root,d^1);
}
return key(root) ^ x;
}
/*int query(int x)
{
int root = 0;
for(int k=21;k>=1;k--)
{
int d = (x&(1<<k-1))>>k-1; d^=1;
if(son(root,d)) root = son(root,d);
else root = son(root,d^1);
}
return key(root) ^ x;
}*/
}trie;
int _xor()
{
trie.clear();
//trie.insert(0); // musn't insert the first one !!! coz 0 is possibly considered!!
int ans = 0; trie.insert(a[1]);
for(int i=2;i<=n;i++) // start from the 2nd !!! coz the existence needs to be provided!!
{
smax(ans , trie.query(a[i]));
trie.insert(a[i]);
}
return ans;
}
int cnt[maxbit];
bool disable[maxn];
int _and()
{
int ans = 0;
memset(cnt,0,sizeof(cnt));
memset(disable,0,sizeof(disable));
for(int i=1;i<=n;i++)
for(int p=1;p<=20;p++)
if(a[i]&(1<<p-1)) cnt[p]++;
for(int p=20;p>=1;p--) if(cnt[p]>=2)
{
ans |= (1<<p-1);
for(int i=1;i<=n;i++) if(!disable[i])
if(!(a[i]&(1<<p-1)))
{
disable[i]=true;
for(int pp=1;pp<=20;pp++)
if(a[i]&(1<<pp-1)) cnt[pp]--;
}
}
return ans;
}
bool g[1<<maxbit];
int _or()
{
memset(g,0,sizeof(g));
int highest=0,tmp=*max_element(a+1,a+n+1);
while(tmp) highest++,tmp>>=1;
int lim = 1<<highest;
for(int i=1;i<=n;i++) g[a[i]]++;
for(int p=1;p<=20;p++)
for(int s=0;s<lim;s++)
if(s&(1<<p-1)) g[s-(1<<p-1)]|=g[s];
int ans = 0;
for(int i=1;i<=n;i++)
{
int now = a[i] , to = 0;
for(int p=20;p>=1;p--) if(!(now&(1<<p-1)))
{
to |= (1<<p-1);
if(!g[to]) to -= (1<<p-1);
}
smax(ans,now|to);
}
return ans;
}
int main()
{
freopen("maximum.in","r",stdin);
freopen("maximum.out","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
int op;
scanf("%d%d",&n,&op);
for(int i=1;i<=n;i++) scanf("%d",a+i);
switch(op)
{
case 1: printf("%d",_and()); break;
case 2: printf("%d",_xor()); break;
case 3: printf("%d",_or()); break;
}
if(T) putchar('\n');
}
return 0;
}
T2:
题意:在数轴上有n个点,每个点有一个初始方向,速度都是1,然后询问第i个点在t时刻的位置
首先明确,对于任意一个点,最终的排名是不变的,并且相撞时相当于两个点交换了位置。
那么把所有点分成向左和向右两种,每次查询第i个点原来的顺序后,现在要找一个位置,使得向左走的点和向右走的点个数之和是原来的排名。
二分答案。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
const int INF=0x3f3f3f3f;
const int maxn = 200005;
inline void read(int &x)
{
x = 0;
int flag = 1;
char ch = getchar();
while(ch<'0' || ch>'9')
{
ch=getchar();
if(ch == '-') flag = -1;
}
while(ch>='0' && ch<='9')
{
x*=10; x+=ch-'0';
ch = getchar();
}
x *= flag;
}
int n,q;
LL lt[maxn],rt[maxn],mt[maxn];
int pos[maxn];
int mxl,mxr,mxm;
int main()
{
freopen("bridge.in","r",stdin);
freopen("bridge.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++) read(pos[i]),mt[++mxm]=pos[i];
for(int i=1;i<=n;i++)
{
int d;
read(d);
if(d) rt[++mxr] = pos[i];
else lt[++mxl] = pos[i];
}
sort(lt+1,lt+mxl+1);
sort(rt+1,rt+mxr+1);
sort(mt+1,mt+mxm+1);
read(q);
while(q--)
{
int x,y;
read(x); read(y); x++;
int standard = lower_bound(mt+1,mt+mxm+1,pos[x])-mt;
LL l = (LL)mt[1] - y , r = (LL)mt[n] + y;
while(l<r)
{
LL m = (l+r)>>1;
int tmp = (upper_bound(lt+1,lt+mxl+1,m+y)-lt-1) + (upper_bound(rt+1,rt+mxr+1,m-y)-rt-1);
if(tmp < standard) l=m+1;
else r=m;
}
printf("%d",(int)l);
if(q) putchar('\n');
}
return 0;
}
T3:
题意:有n个同学,每个同学有一个值,求分成任意组数每组的极差之和不超过k的方案数。
很迷的一道DP。。。
a数组排序后,用f[i][j][k]表示处理到第i个数,有j个还可以填数的组,当前的极差之和为k的方案数。
有四种转移方式:
- 新建一个组,不加入任何数
- 新建一个组,加入一个数,并且该集合只有这一个数
- 加入一个已有的组,不作为最大值
- 加入一个已有的组,并且作为最大值
用差分加速。
把 Sj - Si 差分成很多段,每一次加入之后,当前还有j个未封顶的集合,每一个集合都加上(Ai - Ai-1),总的代价 (Ai - Ai-1) * j
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
const int INF=0x3f3f3f3f;
const int mod = 1000000007;
const int maxn = 205;
const int maxk = 1005;
int n,m;
int s[maxn];
int f[2][maxn][maxk];
int main()
{
freopen("group.in","r",stdin);
freopen("group.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",s+i);
sort(s+1,s+n+1);
int now=0,last=1;
f[now][1][0]=f[now][0][0]=1; // the first number into given to a new but not settled or a new settled set!!
for(int i=2;i<=n;i++)
{
swap(now,last);
memset(f[now],0,sizeof(f[now]));
for(int j=0;j<=n;j++)
for(int k=0;k<=m;k++)
{
int base = f[last][j][k];
if(!base) continue;
int to = (s[i]-s[i-1])*j + k; // delta composition
if(to>m) break;
(f[now][j][to]+=base)%=mod; // a new set with only one element
(f[now][j+1][to]+=base)%=mod; // a new set empty and unlimited
(f[now][j][to]+=(LL)base*j%mod)%=mod; // into an existed set but now the maximum
if(j) (f[now][j-1][to]+=(LL)base*j%mod)%=mod; // into an existed set and to be the maximum element
}
}
int ans = 0;
for(int k=0;k<=m;k++) (ans+=f[now][0][k])%=mod;
printf("%d",ans);
return 0;
}