2019牛客多校第五场

A.digits 2

直接输出 n n n n n n就好

#include<bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int T,n,i;
int main()
{
    cin>>T;
    while (T--)
    {
        cin>>n;
        fo(i,1,n) cout<<n;
        cout<<endl;
    }
    return 0;
}

B.generator 1

没想到真的是裸的快速幂。。
不过这里要用以 10 10 10为底
比如 e 234 = ( e 100 ) 2 ∗ ( e 10 ) 3 ∗ ( e 1 ) 4 e^{234}=(e^{100})^2*(e^{10})^3*(e^1)^4 e234=(e100)2(e10)3(e1)4
所以 e e e每次乘 10 10 10次, a n s ans ans每次乘若干次
这里还有几个优化:
1、计算 e e e 10 10 10次可以用二进制做法( e 10 = e 8 ∗ e 2 e^{10}=e^8*e^2 e10=e8e2
2、计算 a n s ans ans也可以用二进制做法 ( e 100 ) 6 = ( e 100 ) 4 ∗ ( e 100 ) 2 (e^{100})^6=(e^{100})^4*(e^{100})^2 (e100)6=(e100)4(e100)2
如果不用这个优化可能会T(按照题解的意思)(似乎还有人更慢的233333)
不过常数优秀的话暴力算也没啥问题(卡这个就没意思了吧)
这个题如果暴力转换二进制估计就走远了。。就算能写出来也是T?
不过还有数学大佬大力手推循环节真的nb嗷
(其实循环节也很好想, a n − 1 a_{n-1} an1 a n − 2 a_{n-2} an2作为二元组最多也就 p 2 p^2 p2种取法,一旦重复就会产生循环节)

C.generator 2

利用高中数列知识我们可以求出数列的通项公式(利用特征根法)
注意这里要特判掉 a = 1 a=1 a=1的情况
然后经过一系列变形之后可以得到一个 a x ≡ b ( m o d a^x \equiv b(mod axb(mod p ) p) p)的方程
这是一个 B S G S BSGS BSGS算法的裸题
当然这里要用到神奇的预处理。。
假设需要解的方程为 a x ≡ b a^x \equiv b axb,首先改写为 a x t 1 − t 2 ≡ b a^{xt_1-t_2} \equiv b axt1t2b
等价于 a x t 1 ≡ b a t 2 a^{xt_1} \equiv ba^{t_2} axt1bat2,其中 t 1 = 1000 , t 2 &lt; 1000 t_1=1000,t_2&lt;1000 t1=1000,t2<1000
然后可以把左边全部先预处理出来,然后枚举 t 2 t_2 t2,用各种做法(二分、哈希、玄学)把所有解搜出来就好了

F.maximum clique 1

这个图的补图是一个二分图。。
(我都想到这个图转换成补图似乎有点意思,可是就是没想到是二分图)
这还是离散考试的内容。。仅有1位二进制不同的点互相连边组成的图是二分图,就是按照1的个数的奇偶性划分
然后。。就没了,裸的二分图最大独立集

#include <iostream>
#include <vector>
#include <cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int N=100050;
const int M=100050;
int a[10000],f[10000],g[10000];
int x,num,i,j,nn;
int n,m;
struct Edge{
    int v,next;
}edge[M];
int cnt,head[N];
void init(){
    cnt=0;
    memset(head,-1,sizeof(head));
}
void addEdge(int u,int v){
    edge[cnt]=Edge{v,head[u]};
    head[u]=cnt++;
}
//Left表示右边点集的左边匹配点
int Left[N],Right[N];
//S T分别表示左右点集是否已匹配
bool S[N],T[N];
//X Y表示左右两边的最小覆盖点集
vector<int> X,Y;
bool dfs(int u){
    S[u]=true;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(!T[v]){
            T[v]=true;
            if(Left[v]==-1 || dfs(Left[v])){
                Left[v]=u;
                Right[u]=v;
                return true;
            }
        }
    }
    return false;
}
int ans;
void solve(){
    memset(S,0,sizeof(S));
    memset(T,0,sizeof(T));
    memset(Left,-1,sizeof(Left));
    memset(Right,-1,sizeof(Right));

    memset(S,0,sizeof(S));
    memset(T,0,sizeof(T));
    //左边没有匹配的点再增广下去
    for(int i=1;i<=n;i++){
        if(Right[i]==-1){
            dfs(i);
        }
    }
    //此时最小点覆盖集即是左边没标记的点
    for(int u=1;u<=n;u++){
        if(!S[u]){
            X.push_back(u);
        }
    }
    //和右边已标记的点
    for(int v=1;v<=m;v++){
        if(T[v]){
            Y.push_back(v);
        }
    }
}

int main(){
    init();
    scanf("%d",&nn);
    fo(i,1,nn) scanf("%d",&a[i]);
    n = m = 0;
    fo(i,1,nn)
    {
        x = a[i]; num = 0;
        while (x)
        {
            if (x&1) num++; x >>= 1;
        }
        if (num % 2 == 0) f[++n] = a[i]; else g[++m] = a[i];
    }
    
    fo(i,1,n)
    fo(j,1,m)
    {
        x = f[i] ^ g[j]; num = 0;
        while (x) {num += x & 1; x >>= 1;}
        if (num == 1) addEdge(i,j);
    }
    
    solve();//最小点覆盖集,补集即是最大独立集
    int ans = nn-(X.size()+Y.size());
    printf("%d\n",ans);
    for(int i=0;i<X.size();i++) f[X[i]] = -1;
    for(int i=0;i<Y.size();i++) g[Y[i]] = -1;
    fo(i,1,n) if (f[i] != -1) cout<<f[i]<<" ";
    fo(i,1,m) if (g[i] != -1) cout<<g[i]<<" ";
    cout<<endl;
    return 0;
}

G.subsequence 1

首先用 d p dp dp找出长度相等的子序列
f [ i ] [ j ] f[i][j] f[i][j]表示 s s s串匹配到第 i i i位, t t t串匹配到第 j j j位,并且目前还等于的方案数
g [ i ] [ j ] g[i][j] g[i][j]表示 s s s串匹配到第 i i i位, t t t串匹配到第 j j j位,并且已经严格大于的方案数
n 2 n^2 n2转移一下就好
然后用组合数找出长度比 t t t长的情况

#include<bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define MOD 998244353
#define N 3005
using namespace std;
int T,n,m,i,j,l;
long long res;
long long a[N],b[N],f[N],g[N],f_t[N],g_t[N],c[N][N];
char ch[N];
void init()
{
    c[0][0] = 1;
    fo(i,1,3000)
    {
        c[i][0] = 1;
        fo(j,1,i) c[i][j] = (c[i-1][j-1] + c[i-1][j]) % MOD;
    }
}
int main()
{
    init();
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d",&n,&m);
        scanf("%s",ch+1); fo(i,1,n) a[i] = ch[i] - '0';
        scanf("%s",ch+1); fo(i,1,m) b[i] = ch[i] - '0';
        res = 0;
        fo(i,1,n)
            if (a[i] != 0)
            {
                fo(j,m,n-i)
                    res = (res + c[n-i][j]) % MOD;
            }
        fo(i,0,n) f[i] = g[i] = f_t[i] = g_t[i] = 0;
        fo(i,1,n)
        {
            f[0] = 1;
            fo(j,1,m)
                {
                    if (j == 1 && a[i] == 0) continue;
                    if (a[i] == b[j]) f_t[j] = (f_t[j] + f[j-1]) % MOD;
                    if (a[i] > b[j]) g_t[j] = (g_t[j] + f[j-1]) % MOD;
                    g_t[j] = (g_t[j] + g[j-1]) % MOD;
                }
            fo(j,0,m)
                {
                    f[j] = (f[j] + f_t[j]) % MOD;
                    g[j] = (g[j] + g_t[j]) % MOD;
                    f_t[j] = g_t[j] = 0;
                }
        }
        res = (res + g[m]) % MOD;
        cout<<res<<endl;
    }
    return 0;
}

H.subsequence 2

大力猜测如果有答案则唯一(其实不唯一也没关系吧。。)
这个题如果你想到了拓扑排序就很好做。。因为每次实际上是给了你若干个点的相对顺序
比如 a a a b b a aaabba aaabba,实际上可以看成是 a 1 a 2 a 3 b 1 b 2 a 4 a_1a_2a_3b_1b_2a_4 a1a2a3b1b2a4
当跑拓扑的时候出现环(其实就是最后没跑完)那么说明无解
注意这里的无解情况有很多种,不仅仅只有出现次数不一致,还有可能是顺序非法(其实直接判断拓扑结果就好了)

#include<bits/stdc++.h>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define MOD 998244353
#define N 30005
using namespace std;
int n,m,t,i,j,num1,num2,k,nw,nt;
int len[N],x[N],y[N],num[N],color[N],f[N],edgein[N];
char s[N],ch[N],res[N];
vector<int> chh[N],id[N],e[N];
queue<int> q;
int main()
{
    scanf("%d%d",&n,&m);
    t = m * (m - 1) / 2;
    fo(i,1,t)
    {
        scanf("%s%d",s+1,&len[i]);
        gets(ch+1);
        gets(ch+1);
        fo(j,1,len[i]) chh[i].push_back(ch[j]-'a'+1);
        x[i] = s[1] - 'a' + 1; y[i] = s[2] - 'a' + 1;
        num1 = num2 = 0;
        fo(j,1,len[i]) if (ch[j] -'a' + 1 == x[i]) num1++; else num2++;
        if (num[x[i]] == 0 || num[x[i]] == num1)
            num[x[i]] = num1; else {printf("-1\n"); return 0;}
        if (num[y[i]] == 0 || num[y[i]] == num2)
            num[y[i]] = num2; else {printf("-1\n"); return 0;}
    }
    num1 = 0;
    fo(i,1,m) num1 += num[i];
    if (num1 != n) {printf("-1\n"); return 0;}
    k = 0;
    fo(i,1,m)
        fo(j,1,num[i])
        {
            k++;
            id[i].push_back(k);
            color[k] = i;
        }
    fo(i,1,t)
    {
        num1 = num2 = -1;
        fo(j,1,len[i])
        {
            if (chh[i][j-1] == x[i])
            {
                num1++; f[j] = id[x[i]][num1];
            }   else
            {
                num2++; f[j] = id[y[i]][num2];
            }
             
        }
        fo(j,1,len[i]-1) {e[f[j]].push_back(f[j+1]); edgein[f[j+1]]++;}
    }
    fo(i,1,k) if (edgein[i] == 0) q.push(i);
    k = 0;
    while (!q.empty())
    {
        nw = q.front(); q.pop();
        k++; res[k] = color[nw]-1+'a';
        for (i = 0;i < e[nw].size(); i++)
            {
                nt = e[nw][i];
                edgein[nt]--;
                if (edgein[nt] == 0) q.push(nt);
            }
    }
    if (k != n) printf("-1"); else
    fo(i,1,k) printf("%c",res[i]);
    printf("\n");
    return 0;
}

I.three points I

首先大力猜测通过各种变换,三角形的其中一个顶点一定可以和矩形的角重合
然后暴力枚举哪条边卡在边界上(也就是枚举第二个点在边界的位置)
然后就可以算出第三个点的位置
注意1:第二个点的位置有可能有两种情况(有可能在右下,有可能在左上)
哦不过针对这个问题,我的做法是将坐标关于 y = x y=x y=x作对称然后再算一遍就好了(就是把矩形的长宽对调一下)
注意2:第三个点的位置有四种情况(直线的两侧各两种)
这里有一个技巧,不需要每次复杂地讨论解是否合法,直接把三个点算出来 c h e c k check check一下就好(注意 e p s eps eps
这个题最坑的就是输出要和输入对应,如果编程习惯优秀的话可能没啥问题
而我选择暴力枚举六种情况。。。
ps赛场上狂wa的原因是,我们默认了卡在边界上的边是最长的。。实际上三条边都有可能卡在边界上

#include <bits/stdc++.h>
#define PI acos(-1.0)
#define eps 1e-8
using namespace std;
long double w, h, a, b, c ,x,y,z;
long double ax, ay, bx, by, cx, cy;
long double ang1, ang2, ang3, ang4;
bool pos_check(){
    if (ax - w <= eps && ax >= -eps && bx - w <= eps && bx >= -eps
        &&  ay - h <= eps && ay >= -eps && by - h <= eps && by >= -eps) return true;
    return false;
}
bool check(long double a,long double b,long double c){/*
                                                                                   if (a < b) swap(a, b);
                                                                                   if (a < c) swap(a, c);*/
    long double A = acos( (c*c+b*b-a*a)/(2*b*c) );
    // 1
    if (a <= w){
        ax = a, ay = 0;
        ang1 = 0;
    }
    else{
        ax = w, ay = sqrt(a*a - w*w);
        ang1 = ang1 = acos(w/a);;
    }
    ang2 = acos( (a*a+b*b-c*c) / (2*a*b) );
    ang3 = ang2 + ang1;
    bx = b*cos(ang3);
    by = b*sin(ang3);
    if (pos_check()) return true;
     
    // 2
    ang3 = ang1 - ang2;
    bx = b*cos(ang3);
    by = b*sin(ang3);
    if (pos_check()) return true;
     
    // 3
    swap(b, c);
    ang2 = acos( (a*a+b*b-c*c) / (2*a*b) );
    ang3 = ang2 + ang1;
    bx = b*cos(ang3);
    by = b*sin(ang3);
    if (pos_check()) return true;
     
    // 4
    ang3 = ang1 - ang2;
    bx = b*cos(ang3);
    by = b*sin(ang3);
    if (pos_check()) return true;
    return false;
}
bool checkkkk(long double a,long double b,long double c,long double d,long double e,long double f)
{
    long double s1 = sqrt((a-c)*(a-c)+(b-d)*(b-d));
    long double s2 = sqrt((a-e)*(a-e)+(b-f)*(b-f));
    long double s3 = sqrt((c-e)*(c-e)+(d-f)*(d-f));
    if (fabs(s1-x) <= eps && fabs(s2-y) <= eps && fabs(s3-z) <= eps)
    {
        printf("%.12Lf %.12Lf %.12Lf %.12Lf %.12Lf %.12Lf\n", a, b, c, d, e, f);
        return true;
    }
    return false;
}
bool check2(int op,long double a,long double b,long double c)
{
    bool flag;
    if (op == 0) flag = check(a,b,c);
    else
    {
        swap(w,h);
        flag = check(a,b,c);
        swap(ax,ay); swap(bx,by); swap(cx,cy);
        swap(w,h);
    }
    if (!flag) return false;
    //check6种对应关系
    if (checkkkk(ax, ay, bx, by, cx, cy)) return true;
    if (checkkkk(ax, ay, cx, cy, bx, by)) return true;
    if (checkkkk(bx, by, ax, ay, cx, cy)) return true;
    if (checkkkk(bx, by, cx, cy, ax, ay)) return true;
    if (checkkkk(cx, cy, ax, ay, bx, by)) return true;
    if (checkkkk(cx, cy, bx, by, ax, ay)) return true;
    return false;
}
int main () {
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%Lf%Lf%Lf%Lf%Lf", &w, &h, &a, &b, &c);
        x = a; y = b; z = c;
        //枚举长宽以及卡在边界上的边
        if (check2(0,a,b,c)) continue;
        if (check2(1,a,b,c)) continue;
        if (check2(0,b,a,c)) continue;
        if (check2(1,b,a,c)) continue;
        if (check2(0,c,a,b)) continue;
        if (check2(1,c,a,b)) continue;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值