牛客多校第五场部分题题解

B.矩阵快速幂-十进制

  • 题目:generator 1

  • 链接:https://ac.nowcoder.com/acm/contest/885/B

  • 大意:告诉你 x 0 , x 1 , a , b , 且 x i = a ∗ x i − 1 + b ∗ x i − 2 , n ∈ [ 1 , 1 0 1 0 6 ] , m o d , 让 你 求 出 a n % m o d x_0,x_1,a,b,且x_i=a*x_{i-1}+b*x_{i-2},n∈[1,10^{10^6}],mod,让你求出a_n\%mod x0,x1,a,b,xi=axi1+bxi2,n[1,10106],mod,an%mod

  • 分析:矩阵快速幂就可以,然而矩阵快速幂需要把指数化为二进制,但是这里给出的指数是十进制大数,所以使用10进制版的矩阵快速幂,注意常数优化

#pragma GCC optimize(3)
#include<bits/stdc++.h>
 
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const int maxm=1e7+10;
long long x0,x1,a,b,mod;
char o[maxn];
int s[maxn];
int sts=1,ens;
 
 
namespace IO{
    #define BUF_SIZE 100000
    #define OUT_SIZE 100000
    #define ll long long
 
    bool IOerror=0;
    inline char nc(){
        static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
        if (p1==pend){
            p1=buf; pend=buf+fread(buf,1,BUF_SIZE,stdin);
            if (pend==p1){IOerror=1;return -1;}
        }
        return *p1++;
    }
    inline bool blank(char ch){return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';}
    inline bool read(int &x){
        bool sign=0; char ch=nc(); x=0;
        for (;blank(ch);ch=nc());
        if (IOerror) return false;
        if (ch=='-')sign=1,ch=nc();
        for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0';
        if (sign)x=-x; return true;
    }
    inline bool read(ll &x){
        bool sign=0; char ch=nc(); x=0;
        for (;blank(ch);ch=nc());
        if (IOerror) return false;
        if (ch=='-')sign=1,ch=nc();
        for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0';
        if (sign)x=-x; return true;
    }
    inline bool read(double &x){
        bool sign=0; char ch=nc(); x=0;
        for (;blank(ch);ch=nc());
        if (IOerror) return false;
        if (ch=='-')sign=1,ch=nc();
        for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0';
        if (ch=='.'){
            double tmp=1; ch=nc();
            for (;ch>='0'&&ch<='9';ch=nc())tmp/=10.0,x+=tmp*(ch-'0');
        }
        if (sign)x=-x; return true;
    }
    inline bool read(char *s){
        char ch=nc();
        for (;blank(ch);ch=nc());
        if (IOerror) return false;
        for (;!blank(ch)&&!IOerror;ch=nc())*s++=ch;
        *s=0; return true;
    }
    inline void read(char &c){
        for (c=nc();blank(c);c=nc());
        if (IOerror){c=-1;return;}
    }
    //fwrite->write
    struct Ostream_fwrite{
        char *buf,*p1,*pend;
        Ostream_fwrite(){buf=new char[BUF_SIZE];p1=buf;pend=buf+BUF_SIZE;}
        void out(char ch){
            if (p1==pend){
                fwrite(buf,1,BUF_SIZE,stdout);p1=buf;
            }
            *p1++=ch;
        }
        void print(int x){
            static char s[15],*s1;s1=s;
            if (!x)*s1++='0';if (x<0)out('-'),x=-x;
            while(x)*s1++=x%10+'0',x/=10;
            while(s1--!=s)out(*s1);
        }
        void println(int x){
            static char s[15],*s1;s1=s;
            if (!x)*s1++='0';if (x<0)out('-'),x=-x;
            while(x)*s1++=x%10+'0',x/=10;
            while(s1--!=s)out(*s1); out('\n');
        }
        void print(ll x){
            static char s[25],*s1;s1=s;
            if (!x)*s1++='0';if (x<0)out('-'),x=-x;
            while(x)*s1++=x%10+'0',x/=10;
            while(s1--!=s)out(*s1);
        }
        void println(ll x){
            static char s[25],*s1;s1=s;
            if (!x)*s1++='0';if (x<0)out('-'),x=-x;
            while(x)*s1++=x%10+'0',x/=10;
            while(s1--!=s)out(*s1); out('\n');
        }
        void print(double x,int y){
            static ll mul[]={1,10,100,1000,10000,100000,1000000,10000000,100000000,
                1000000000,10000000000LL,100000000000LL,1000000000000LL,10000000000000LL,
                100000000000000LL,1000000000000000LL,10000000000000000LL,100000000000000000LL};
            if (x<-1e-12)out('-'),x=-x;x*=mul[y];
            ll x1=(ll)floor(x); if (x-floor(x)>=0.5)++x1;
            ll x2=x1/mul[y],x3=x1-x2*mul[y]; print(x2);
            if (y>0){out('.'); for (size_t i=1;i<y&&x3*mul[i]<mul[y];out('0'),++i) {}; print(x3);}
        }
        void println(double x,int y){print(x,y);out('\n');}
        void print(char *s){while (*s)out(*s++);}
        void println(char *s){while (*s)out(*s++);out('\n');}
        void flush(){if (p1!=buf){fwrite(buf,1,p1-buf,stdout);p1=buf;}}
        ~Ostream_fwrite(){flush();}
    }Ostream;
    inline void print(int x){Ostream.print(x);}
    inline void println(int x){Ostream.println(x);}
    inline void print(char x){Ostream.out(x);}
    inline void println(char x){Ostream.out(x);Ostream.out('\n');}
    inline void print(ll x){Ostream.print(x);}
    inline void println(ll x){Ostream.println(x);}
    inline void print(double x,int y){Ostream.print(x,y);}
    inline void println(double x,int y){Ostream.println(x,y);}
    inline void print(char *s){Ostream.print(s);}
    inline void println(char *s){Ostream.println(s);}
    inline void println(){Ostream.out('\n');}
    inline void flush(){Ostream.flush();}
    #undef ll
    #undef OUT_SIZE
    #undef BUF_SIZE
};
using namespace IO;
 
 
struct matrix{
    ll mat[3][3];int siz;
    matrix(int a=3){
        siz=a-1;
        memset(mat,0,sizeof(mat));
    }
    matrix operator*(const matrix &b){
        matrix res;
        for(int i=1;i<=siz;i++){
            for(int k=1;k<=siz;k++){
                if(mat[i][k]){
                    for(int j=1;j<=siz;j++){
                        if(b.mat[k][j]){
                            res.mat[i][j]=(res.mat[i][j]+(mat[i][k]*b.mat[k][j]));
                            if(res.mat[i][j]>=mod) res.mat[i][j]%=mod;
                        }
                    }
                }
            }
        }
        return res;
    }
    matrix pow(long long k){
        matrix res,tmp=*this;
        for(int i=1;i<=siz;i++) res.mat[i][i]=1;
        while(k){
            if(k&1) res=res*tmp;
            tmp=tmp*tmp;
            k>>=1;
        }
        return res;
    }
    void print(){
        for(int i=1;i<=siz;i++){
            for(int j=1;j<=siz;j++){
                printf("%lld%c",mat[i][j],j==siz?'\n':' ');
            }
        }
    }
}cur,d,e,res;
 
void jianyi()
{
    int last=-1;
    if(s[ens]>=1) {s[ens]-=1;return;}
    else{
        for(int i=ens;i>=sts;i--) {
            if(s[i]) {s[i]-=1;break;}
            else s[i]=9;
        }
        if(!s[sts]) sts++;
         
    }
}
 
int main()
{
    //freopen("/Users/wzw/Desktop/ACM/1.in","r",stdin);
    read(x0);read(x1);read(a);read(b);
    read(o+1);read(mod);
    ens=strlen(o+1);
    for(int i=1;i<=ens;i++) s[i]=o[i]-'0';
    jianyi();
 
    cur.mat[1][1]=a;cur.mat[1][2]=b;cur.mat[2][1]=1;cur.mat[2][2]=0;
    d.mat[1][1]=x1;d.mat[2][1]=x0;
    res.mat[1][1]=res.mat[2][2]=1;
 
    for(int i=ens;i>=sts;i--) {
        auto tmp=cur.pow(s[i]);
        res=res*tmp;
        cur=tmp*cur.pow(10-s[i]);
         
    }
    res=res*d;
    printf("%lld\n",res.mat[1][1]);
}

C.BSGS-略作修改

  • 题目:generator 2

  • 链接:https://ac.nowcoder.com/acm/contest/885/C

  • 大意:给出 a , x 0 , b , p a,x_0,b,p a,x0,b,p,告诉你 x i = ( a ∗ x i − 1 + b ) m o d   p x_i=(a*x_{i-1}+b)mod\ p xi=(axi1+b)mod p,给你Q(1000)个询问,每个询问包含一个v(1e18),问你最小的 i , 使 得 x i = v i,使得x_i=v i,使xi=v

  • 分析:a=0特例特判下,a!=0可以先根据高中知识求出通项公式,转化为一个bsgs的问题但是bsgs传统分块方式都是分成 n ∗ n \sqrt n*\sqrt n n n 的,而bsgs的初始化复杂度为 n B l o c k nBlock nBlock,查询复杂度szBlock,所以这题查询比较多,有选择地决定快的大小就可以了。

#include<bits/stdc++.h>
#include<unordered_map>
typedef long long ll;
 
const int maxn = 1e7 + 7;
const int inf = 0x3f3f3f3f;
using namespace std;
 
ll n, x0, a, b, mod;
ll invb, inva;
 
 
 
ll qpow(ll x, ll n)
{
    ll res = 1;
    while (n)
    {
        if (n & 1) res = res * x%mod;
        x = x * x%mod; n >>= 1;
    }
    return res;
}
 
unordered_map<int, int>ma;
ll bsgs(ll a, ll b, ll p) {
    if (b == 1) return 0;
    int res = inf;
    for (int i = 0; i <= 999; i++) {
        //int id = find(b);
        auto id = ma.find(b);
        if (id != ma.end()) res = min(res, id->second - i);
        b = b * a%p;
    }
    if (res == inf) return -1;
    return res;
}
 
void init_bsgs()
{
    int st = qpow(a, 1000), x = 1;
    for (int i = 1; i <= 1000001; i++)
    {
        x = (ll)x*st%mod;
        if (ma.find(x)==ma.end()) ma[x]=i * 1000;
    }
}
 
int main()
{
    int T; scanf("%d", &T);
    while (T--)
    {
        scanf("%lld%lld%lld%lld%lld", &n, &x0, &a, &b, &mod);
        ma.clear();
        int q; scanf("%d", &q); init_bsgs();
        ll tmp = qpow((a - 1 + mod) % mod, mod - 2)*b%mod;
        ll inv = qpow((x0 + tmp) % mod, mod - 2);
        invb = qpow(b, mod - 2);
        while (q--)
        {
            ll v; scanf("%lld", &v);
            if (a == 0)
            {
                if (v == x0) puts("0");
                else if (v == b)
                {
                    if (n != 1) puts("1");
                    else puts("-1");
                }
                else puts("-1");
                continue;
            }
            if (a == 1)
            {
                if (b == 0)
                {
                    if (v == x0) puts("0");
                    else puts("-1");
                }
                else
                {
                    ll res = (v - x0 + mod) % mod*invb%mod;
                    if (res < n) printf("%lld\n", res);
                    else puts("-1");
                }
                continue;
            }
            ll B = (v + tmp) % mod*inv%mod;
            ll res = bsgs(a, B, mod);
            if (res == -1 || res >= n) puts("-1");
            else printf("%lld\n", res);
        }
    }
    return 0;
}

E.子集最大独立集size之和

  • 题目:independent set 1

  • 链接:https://ac.nowcoder.com/acm/contest/885/E

  • 大意:给定一张最多26结点的图,求所有子集的最大独立集size之和

  • 分析: 比赛的时候完全没想到怎么做,后来看题解说是状压dp就完了,但无奈我太菜,不会这个状压dp。这题算是考研ACM的功底吧。设A是图中的一个点集,我们要遍历图的所有子集,所以在计算A之前,我们已经计算出了所有A的子集(别问为什么),设p是A中的一个点,则A的答案中要么包含p,要么不包含p,于是—>dp[A]=max(dp[A中除去p和与p有邻边的点]+1,dp[A中除去p]),A中除去p好说,用A的状压减去(1<<p)就好了,但是如何得到[A中除去p和与p有邻边的点]的状压呢,可以算出所有与p点有临边的点集的状压ban,那么[A中除去p和与p有邻边的点]就等于A & (~(ban|(1<<p))),于是可以写代码了,

#include<bits/stdc++.h>
#define F(i,a,b) for(int i=(a);i<=int(b);++i)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = int(27);
typedef unsigned char si;
int ban[maxn];
int n, m;
si dp[1 << 26];
int lowbit(int x) { return x & -x; }
int lowbit2(int x)
{
	int p = 0, w = 1;
	while ((x&w) == 0)
	{
		w <<= 1;
		++p;
	}
	return p;
}
#define endl '\n'
int main()
{
#ifndef endl
	freopen("C:\\Users\\VULCAN\\Desktop\\data.in", "r", stdin);
	cout << "************************************Local Test*********************************" << endl;
#endif // !endl
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);

	int T(1), cas(0);
	while (cas, T--)
	{
		cin >> n >> m;
		F(i, 0, n - 1)ban[i] = 1 << i;
		F(i, 1, m)
		{
			int a, b; cin >> a >> b;
			ban[a] |= 1 << b;
			ban[b] |= 1 << a;
		}
		int ans = 0;
		F(i, 1, (1 << n) - 1)
		{
			auto tmp = dp[i &(~ ban[lowbit2(i)])];
			dp[i] = max(dp[i - lowbit(i)], ++tmp);
			ans += dp[i];
		}
		cout << ans << '\n';
	}
	return 0;
}
//What to Debug
/*
-1.最好把全部warning都X掉,否则:https://vjudge.net/solution/19887176
0.看看自己是否有可能需要快读,禁endl
1.数组越界,爆int,浮点精度(查看精度是否达到题目要求,看有没有浮点数比较:eps),取模操作,初始化数组,边缘数据,输出格式(cas),强制在线是否更新了las
2.通读代码,代码无逻辑错误
3.读题,找到题意理解失误或算法错误
4.放弃
*/

F. 最大团-二分图-输出方案

  • 题目:maximum clique 1

  • 链接:https://ac.nowcoder.com/acm/contest/885/F

  • 大意:给一个集合(大小5e3),由于是集合,所以每个字都不同,求一个最大子集合使得子集合内每两个点最少有1个二进制bit位不同

  • 分析:额,对于集合内没对bit位有>=1个不同的,在两点之间连一条边,于是题目就变成了在图论中求一个最大团,但是最大团不是NPC问题么,只能指数级别算呀,这可不行。于是可以看这个边有什么性质,图里的边要满足bit差异至少有1个,那么由于元素不同,它反过来就是bit位差异为1,那么补图的某一边存在当且仅当bit差异为1,相当于就是求补图的最大独立集,再想想,bit差异为1????,那岂不是可以根据二进制中1的个数划分为二分图,二分图的最大独立集???那就可以做了,顺便说下我的二分图板子没有给方案的,还得弄弄。

  • cyy的代码:

#include <bits/stdc++.h>
using namespace std;
int n;
int a[5000],c[5000];
vector <int> g[5000];
void color(int u,int rt) {
    for (auto v:g[u]) {
        if (v == rt) continue;
        if (~c[v]) continue;
        c[v] = c[u] ^ 1;
        color(v,u);
    }
}
bool s[5000],t[5000];
int l[5000],r[5000];
bool dfs(int u) {
    s[u] = true;
    for (auto v:g[u]) {
        if (t[v]) continue;
        t[v] = true;
        if (l[v] == -1 || dfs(l[v])) {
            l[v] = u;
            r[u] = v;
            return true;
        }
    }
    return false;
}
void work() {
    memset(l,-1,sizeof(l));
    memset(r,-1,sizeof(r));
    int match = 0;
    for (int i=0;i<n;i++) if (c[i] == 0) {
        memset(t,false,sizeof(t));
        match += dfs(i);
    }
    memset(s,false,sizeof(s));
    memset(t,false,sizeof(t));
    for (int i=0;i<n;i++) if (c[i] == 0 && r[i] == -1) dfs(i);
    printf("%d\n",n-match);
    for (int i=0;i<n;i++) {
        if ( (c[i] == 0 && s[i]) || (c[i] == 1 && !t[i])) printf("%d ",a[i]);
    }
    printf("\n");
}
int main() {
    memset(c,-1,sizeof(c));
    scanf("%d",&n);
    for (int i=0;i<n;i++) scanf("%d",&a[i]);
    sort(a,a+n);
    n = unique(a,a+n) - a;
    for (int i=0;i<n;i++)
        for (int j=0;j<i;j++)
            if (__builtin_popcount(a[i] ^ a[j]) == 1) {
                g[i].push_back(j);
                g[j].push_back(i);
            }
    for (int i=0;i<n;i++) if (c[i] == -1) {
        c[i] = 0;
        color(i,-1);
    }
    work();
    return 0;
}

I.几何-三角形

  • 题目:three points 1

  • 链接:https://ac.nowcoder.com/acm/contest/885/I

  • 大意:给你w,h,a,b,c∈[1,50],要你在w*h的矩形框内,找个三角形三边长为a,b,c

  • 分析:几何白痴,不多说了,直接给队友代码

#include<bits/stdc++.h>
 
using namespace std;
const double eps=1e-8;
#define pi acos(-1.0)
 
int sgn(double k)
{
    return k<-eps?-1:(k<eps?0:1);
}
 
void print(double a) //数值a保留5位四舍五入输出,
{
    char buffer[1000];
    sprintf(buffer+1,"%.10lf",a);  //
    int len=strlen(buffer+1);
    bool ok=false;
    for(int i=1;i<=len;i++) if(!(buffer[i]=='.'||buffer[i]=='0'||buffer[i]=='-')) {ok=true;break;}
    if(ok) printf("%s",buffer+1);
    else printf("%.10lf",0.0);    //
}
 
typedef struct point{  //点结构体
    double x,y;
    point(double a=0,double b=0) {
        x=a;y=b;
    }
    point operator+(point other) {
        return point(x+other.x,y+other.y);
    }
    point operator-(point other) {
        return point(x-other.x,y-other.y);
    }
    point operator*(double k) {
        return point(x*k,y*k);
    }
    double dot(point other) { //点乘,即数量积,内积(ABcos &)
        return x*other.x+y*other.y;
    }
    double cha(point other) { //叉乘,即向量积,外积(ABsin &)
        return x*other.y-y*other.x;
    }
    bool onseg(point p1,point p2) {//判断q是否在线段p1-p2上
        point q=*this;
        return sgn((p1-q).cha(p2-q))==0&&(p1-q).dot(p2-q)<=0;
    }
    friend double dis(point p1,point p2) {     //计算两点距离
        return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
    }
 
    friend point intersect(point p1,point p2,point q1,point q2) {//计算直线p1-p2是否与直线q1-q2的交点
        return p1+(p2-p1)*((q2-q1).cha(q1-p1)/(q2-q1).cha(p2-p1));
    }
 
    friend bool parallel(point p1,point p2,point q1,point q2) { //判断线段p1-p2是否与线段q1-q2平行,重合也会返回true
        return sgn((q2-q1).cha(p2-p1))==0;
    }
 
    friend bool parallel_intersect(point p1,point p2,point q1,point q2) { //判断线段p1-p2和线段q1-q2在平行的情况下是否重合
        return p1.onseg(q1,q2)||p2.onseg(q1,q2)||q1.onseg(p1,p2)||q2.onseg(p1,p2);
    }
 
    friend point extend(point p,double len) { //将向量p长度变为len返回一个新的向量,方向不变
        if(sgn(dis(point(0,0),p))==0) return point(0,len);
        return point(p*(len/dis(point(0,0),p)));
    }
 
    void print() {
        printf("x:%.3lf  y:%.3lf\n",x,y);
    }
}Vector;
 
point rotate(point p1,point p2,double a) //点p2绕着点p1顺时针方向旋转a角度,a为弧度,返回新的point
{
    point vec=p2-p1;
    double xx=vec.x*cos(a)+vec.y*sin(a);
    double yy=vec.y*cos(a)-vec.x*sin(a);
    return point(p1.x+xx,p1.y+yy);
}
 
bool valid(point p1,double w,double h)
{
    return sgn(p1.x)>=0&&sgn(p1.x-w)<=0&&sgn(p1.y)>=0&&sgn(p1.y-h)<=0;
}
 
point ans[3];
bool check(double w,double h,double x,double y,double z,int a,int b,int c)
{
    if(sgn(sqrt(w*w+h*h)-x)<0) return false;
    double angle_y=acos((x*x+z*z-y*y)/(2*x*z));
    if(sgn(x-w)<=0) {   
         
        Vector cur=extend(Vector(x,0),z);
        ans[a]=point(x,0);ans[c]=point(0,0);ans[b]=cur;
 
        point res=rotate(ans[c],ans[b],2*pi-angle_y);
        if(valid(res,w,h)) {ans[b]=res;return true;}
    }else{
        double he=sqrt(x*x-w*w);
        Vector cur=extend(Vector(w,he),z);
        ans[a]=point(w,he);ans[c]=point(0,0);ans[b]=cur;
         
        point res=rotate(ans[c],ans[b],angle_y);
        if(valid(res,w,h)) {ans[b]=res;return true;}
        res=rotate(ans[c],ans[b],2*pi-angle_y);
        if(valid(res,w,h)) {ans[b]=res;return true;}
    }
 
    if(sgn(x-h)<=0) {
        Vector cur=extend(Vector(0,x),z);
        ans[a]=point(0,x);ans[c]=point(0,0);ans[b]=cur;
 
        point res=rotate(ans[c],ans[b],angle_y);
        if(valid(res,w,h)) {ans[b]=res;return true;}
    }else {
        double he=sqrt(x*x-h*h);
        Vector cur=extend(Vector(he,h),z);
        ans[a]=point(he,h);ans[c]=point(0,0);ans[b]=cur;
         
        point res=rotate(ans[c],ans[b],angle_y);
        if(valid(res,w,h)) {ans[b]=res;return true;}
        res=rotate(ans[c],ans[b],2*pi-angle_y);
        if(valid(res,w,h)) {ans[b]=res;return true;}       
    }
    return false;
}
 
void outt()
{
    for(int i=0;i<=2;i++) {
        print(ans[i].x);printf(" ");
        print(ans[i].y);printf(i==2?"\n":" ");
    }
}
 
 
int main()
{
    int t;scanf("%d",&t);
    while(t--) {
        double w,h,a,b,c;scanf("%lf %lf %lf %lf %lf",&w,&h,&a,&b,&c);
        if(check(w,h,a,b,c,0,2,1)) {outt();continue;}
        if(check(w,h,a,c,b,1,2,0)) {outt();continue;}
        if(check(w,h,b,a,c,0,1,2)) {outt();continue;}
        if(check(w,h,b,c,a,2,1,0)) {outt();continue;}
        if(check(w,h,c,a,b,1,0,2)) {outt();continue;}
        if(check(w,h,c,b,a,2,0,1)) {outt();continue;}
        assert(false);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值