问题:输入(m)个(n)位(p)进制数,定义加法和乘法都是模(p)意义下不进位加法乘法运算,求这些数线性组合能得到的最大(p)进制数(一般意义上)
代码:
#include <iostream>
const int N=110;
int n,p;
struct num{
int bits[N];
num(){
for (int i=0; i<n; ++i) bits[i]=0;
}
bool operator !()const{
for (int i=0; i<n; ++i) if (bits[i]) return 0;
return 1;
}
num operator *(const int &u){
num ret=*this;
for (int i=0; i<n; ++i)
ret.bits[i]=ret.bits[i]*u%p;
return ret;
}
num operator -(const num &u){
num ret=*this;
for (int i=0; i<n; ++i){
ret.bits[i]-=u.bits[i];
if (ret.bits[i]<0) ret.bits[i]+=p;
}
return ret;
}
num operator +(const num &u){
num ret=*this;
for (int i=0; i<n; ++i){
ret.bits[i]+=u.bits[i];
if (ret.bits[i]>=p) ret.bits[i]-=p;
}
return ret;
}
friend std::ostream & operator <<(std::ostream & os,const num &_){
for (int i=0; i<n; ++i)
os<<_.bits[i]<<" ";
return os;
}
}base[N],a;
void exgcd(int &x,int &y,int a,int b)
{
if(!b)
{
x=1;
y=0;
return;
}
exgcd(x,y,b,a%b);
int t=x;
x=y;
y=t-a/b*y;
}
void insert(num a){
for (int i=0; i<n; ++i) {
if (!base[i]) {
int xx, yy;
exgcd(xx, yy, a.bits[i], p);
xx=(xx%p+p)%p;
base[i] = a*xx;
break;
}
else if (a.bits[i]%base[i].bits[i]==0){
int tmp=a.bits[i]/base[i].bits[i];
a=a-base[i]*tmp;
}
else{
int xx,yy;
exgcd(xx,yy,a.bits[i],base[i].bits[i]);
xx=(xx%p+p)%p;
yy=(yy%p+p)%p;
base[i]=a*xx+base[i]*yy;
break;
}
}
}
num max_value(){
num ret;
for (int i=0; i<n; ++i)
if (base[i].bits[i]){
for (int j=p-1; j>=0; --j)
if ((j-ret.bits[i])%base[i].bits[i]==0) {
ret=ret+base[i]*((j-ret.bits[i])/base[i].bits[i]);
break;
}
}
return ret;
}
int m;
int main() {
scanf("%d%d",&n,&p);
scanf("%d",&m);
for (int i=1; i<=m; ++i){
for (int j=0; j<n; ++j)
scanf("%d",&a.bits[j]);
insert(a);
for (int i=0; i<n; ++i){
std::cerr<<base[i];
std::cerr<<std::endl;
}
std::cerr<<"insertend"<<std::endl;
}
std::cout<<max_value();
return 0;
}
这个代码也不知道对不对,可能只有p是质数的时候是对的。但是是我之前想法的实现。
struct num{
int bits[N];
}
代表一个p进制数,bits[0]表示最高位。
base[N]是一个线性基
其中base[i]是一个p进制数,表示第i位的基为base[i]
insert(a)的时候从高位往低位扫
假设扫到第i位,分三种情况讨论
Case 1:该位的基还不存在
考虑在二进制的线性基中我们是怎么做的,我们直接将基赋值成当前元素。
但是在p进制中,这一位可以表示的值是(gcd(p,a[i]))的倍数,那么就需要将当前的(a)乘以若干倍之后作为基。
Case 2:基存在,且基可以表示a的第i位
那么仿效二进制的情况,将a的第i位变成0,继续做
Case 3:基存在,且基不能表示a的第i位
那么加入了当前元素之后这一位可以表示的值是(gcd(base[i][i],a[i]))的倍数,我们修改一下基,使基的第i位变为gcd
修改基时需要解(ax+by=gcd(a,b)),可以使用扩展欧几里德算法。
最后算最大值的时候就按位贪心即可,我实现得暴力了一些。
由于我也不是很确定算法的正确性,欢迎来Hack。
Upd:上面那份代码是有锅的,insert的时候可能需要分四类讨论,还有一类是a的第i位可以表示基,那么交换一下基和a。
新的不知道是对还是错的代码
#include <iostream>
const int N=110;
int n,p;
struct num{
int bits[N];
num(){
for (int i=0; i<n; ++i) bits[i]=0;
}
bool operator !()const{
for (int i=0; i<n; ++i) if (bits[i]) return 0;
return 1;
}
num operator *(const int &u){
num ret=*this;
for (int i=0; i<n; ++i)
ret.bits[i]=ret.bits[i]*u%p;
return ret;
}
num operator -(const num &u){
num ret=*this;
for (int i=0; i<n; ++i){
ret.bits[i]-=u.bits[i];
if (ret.bits[i]<0) ret.bits[i]+=p;
}
return ret;
}
num operator +(const num &u){
num ret=*this;
for (int i=0; i<n; ++i){
ret.bits[i]+=u.bits[i];
if (ret.bits[i]>=p) ret.bits[i]-=p;
}
return ret;
}
friend std::ostream & operator <<(std::ostream & os,const num &_){
for (int i=0; i<n; ++i)
os<<_.bits[i]<<" ";
return os;
}
}base[N],a;
void exgcd(int &x,int &y,int a,int b)
{
if(!b)
{
x=1;
y=0;
return;
}
exgcd(x,y,b,a%b);
int t=x;
x=y;
y=t-a/b*y;
}
void insert(num a){
for (int i=0; i<n; ++i) {
if (!a) break;
if (!base[i]) {
int xx, yy;
exgcd(xx, yy, a.bits[i], p);
xx=(xx%p+p)%p;
base[i] = a*xx;
}
else if (a.bits[i]%base[i].bits[i]==0);
else if (base[i].bits[i]%a.bits[i]==0) std::swap(a,base[i]);
else{
int xx,yy;
exgcd(xx,yy,a.bits[i],base[i].bits[i]);
xx=(xx%p+p)%p;
yy=(yy%p+p)%p;
base[i]=a*xx+base[i]*yy;
}
int tmp=a.bits[i]/base[i].bits[i];
a=a-base[i]*tmp;
}
}
num max_value(){
num ret;
for (int i=0; i<n; ++i)
if (base[i].bits[i]){
for (int j=p-1; j>=0; --j)
if ((j-ret.bits[i])%base[i].bits[i]==0) {
ret=ret+base[i]*((j-ret.bits[i])/base[i].bits[i]);
break;
}
}
return ret;
}
int m;
int main() {
scanf("%d%d",&n,&p);
scanf("%d",&m);
for (int i=1; i<=m; ++i){
for (int j=0; j<n; ++j)
scanf("%d",&a.bits[j]);
insert(a);
for (int i=0; i<n; ++i){
std::cerr<<base[i];
std::cerr<<std::endl;
}
std::cerr<<"insertend"<<std::endl;
}
std::cout<<max_value();
return 0;
}
}