“科大讯飞杯”第十七届同济大学程序设计预选赛暨高校网络友谊赛

比赛传送门

A-张老师和菜哭武的游戏(裴蜀定理)

比赛时光顾着找规律了,因为我发现只要首先选的数有一个是奇数那么就能选到区间 [ 1 , n ] [1,n] [1,n]的所有数,否则就只能选区间内的所有偶数。暴力枚举了样例和自己写的例子,也都对,真是没辙…怪自己没想到去手推一下公式。也可能是只记得exgcd忘记了裴蜀定理?

首先假设选出的两个数为 x 、 y x、y xy并且假设 n n n无穷大,那么设 x > y x>y x>y,模拟一下加入的数的过程:

A A A选择 z 1 = x + y z_1=x+y z1=x+y加入

B B B选择 z 2 = x − y z_2=x-y z2=xy加入

A A A选择 z 3 = z 1 + x = 2 x + y z_3=z_1+x=2x+y z3=z1+x=2x+y

B B B选择 z 4 = z 1 + y = 2 y + x z_4=z_1+y=2y+x z4=z1+y=2y+x

A A A选择 z 1 = z 2 + x = 2 x − y z_1=z_2+x=2x-y z1=z2+x=2xy

那么我们不难发现,实际上除了一开始选择的两个数,后面的数 z z z肯定满足:

z = a x + b y z=ax+by z=ax+by( a , b a,b a,b均为整数)且 z < = n z<=n z<=n

考虑裴蜀定理:若 a , b a,b a,b是整数,那么一定存在整数 x , y x,y x,y,使 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)成立

因此就是看 n n n以内有多少 g c d ( a , b ) gcd(a,b) gcd(a,b),这里 a , b a,b a,b是第一次的两个数,那么只需要判断结果的奇偶性即可

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <math.h>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=2e5+10;

ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}

int main(){
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    ll t,n,a,b;
    cin>>t;
    while(t--){
        cin>>n>>a>>b;
        ll ans=n/gcd(a,b);
        if(ans&1){
            cout<<"Yes"<<endl;
        }else cout<<"No"<<endl;
    }
    return 0;
}
B-伤害计算(模拟)

注意小数化整处理

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <math.h>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=2e5+10;
 
ll f[maxn];
string s,str[1005];
 
void init(){
    for(int i=1;i<=1005;i++)
        f[i]=f[i-1]+i;
}
 
ll solve(string res){
    int pos=0;
    for(int i=0;i<res.size();i++){
        if(res[i]=='d'){
            pos=i;
            break;
        }
    }
    //cout<<pos<<endl;
    if(pos){
        int p=0,q=0;
        for(int i=0;i<pos;i++)
            p=p*10+res[i]-'0';
        for(int i=pos+1;i<res.size();i++)
            q=q*10+res[i]-'0';
        ll qq=f[q]*10/q;
        return p*qq;
    }else{
        ll ans=0;
        for(int i=0;i<res.size();i++){
            ans=ans*10+res[i]-'0';
        }
        return ans*10;
    }
 
}
 
int main(){
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    //ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    init();
    char c;
    s="";
    while((c=getchar())!='\n'){
        if(c=='+') s+=' ';
        else s+=c;
    }
    stringstream ss(s);
    string buf;
    int cnt=0;
    while(ss>>buf){
        str[cnt++]=buf;
    }
    ll ans=0;
    for(int i=0;i<cnt;i++){
        //cout<<str[i]<<endl;
        ans+=solve(str[i]);
    }
    //cout<<ans<<endl;
    int x=ans%10;
    ans/=10;
    if(x) printf("%d.5\n",ans);
    else printf("%d\n",ans);
    return 0;
}
C-张老师的旅行(区间DP)

裸的区间DP

#include <bits/stdc++.h>
using namespace std;
const int maxn=1005;
const int inf=0x3f3f3f3f;
 
int p[maxn],t[maxn];
int d[maxn][maxn][2];
 
int main(){
    int n,m;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>p[i];
    for(int i=1;i<=n;i++){
        cin>>t[i];
        if(!t[i]) m=i;
    }
    memset(d,0x3f,sizeof d);
    d[m][m][0]=d[m][m][1]=0;
    for(int len=2;len<=n;len++)
        for(int i=1;i+len-1<=n;i++){
            int j=i+len-1;
            if(d[i+1][j][0]+p[i+1]-p[i]<=t[i]) d[i][j][0]=min(d[i][j][0],d[i+1][j][0]+p[i+1]-p[i]);
            if(d[i+1][j][1]+p[j]-p[i]<=t[i]) d[i][j][0]=min(d[i][j][0],d[i+1][j][1]+p[j]-p[i]);
 
            if(d[i][j-1][0]+p[j]-p[i]<=t[j]) d[i][j][1]=min(d[i][j][1],d[i][j-1][0]+p[j]-p[i]);
            if(d[i][j-1][1]+p[j]-p[j-1]<=t[j]) d[i][j][1]=min(d[i][j][1],d[i][j-1][1]+p[j]-p[j-1]);
 
  
 
        }
    int ans=min(d[1][n][0],d[1][n][1]);
    cout<<(ans<inf?ans:-1)<<endl;
    return 0;
}
D-车辆调度(搜索)

一开始写暴力的,也就是当时间为 k k k时检查是否符合条件,但是栈溢出了,没办法看了看其他代码,原来有这样一个可剪枝的地方:当有一个目标点停了小车后,每次都操作这个小车,但是不让他移动,这样最后到时间 k k k一定存在

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <math.h>
#include <cstdio>
#include <string>
#include <bitset>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define lowbit(x) (x&(-x))
#define mkp(x,y) make_pair(x,y)
#define mem(a,x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> P;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=2e5+10;

const int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
char g[15][15];
int w,h,k;

bool check(int x,int y,int i){
    if(i==0 && y<=h) return 1;
    else if(i==1 && y>=1) return 1;
    else if(i==2 && x<=w) return 1;
    else if(i==3 && x>=1) return 1;
    return 0;
}

void print(){
    cout<<endl;
    for(int i=1;i<=w;i++){
        for(int j=1;j<=h;j++)
            cout<<g[i][j]<<" ";
        cout<<endl;
    }
    cout<<endl;
}

void dfs(int t){
    if(t==k) return;
    for(int i=1;i<=w;i++)
    for(int j=1;j<=h;j++) if(g[i][j]=='R'){
        for(int k=0;k<4;k++){
            int x=i+dx[k],y=j+dy[k];
            if(x<=0 || x>w || y<=0 || y>h) continue;
            if(g[x][y]=='R' || g[x][y]=='X') continue;
            while(g[x][y]!='R' && g[x][y]!='X' && check(x,y,k)){
                x+=dx[k];
                y+=dy[k];
            }
            x-=dx[k],y-=dy[k];
            if(g[x][y]=='D'){
                cout<<"YES"<<endl;
                exit(0);
            }
            g[i][j]='.';
            g[x][y]='R';
            dfs(t+1);
            g[i][j]='R';
            g[x][y]='.';
        }
    }
}

int main(){
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>h>>w>>k;
    for(int i=1;i<=w;i++)
        for(int j=1;j<=h;j++){
            cin>>g[i][j];
        }
    dfs(0);
    cout<<"NO"<<endl;
    return 0;
}
E-弦(组合数学+卡特兰数)

首先这个模型就是卡特兰数的经典模型之一,然后就是求概率的问题。值得一提的是如果任意选取的方案个数:先给所有的点全排列,然后两两一组,因为有 n ! n! n!组,所以要除掉。然后就是 ( a , b ) , ( b , a ) (a,b),(b,a) (a,b),(b,a)算是同一种情况,这样的重复有 2 n 2^n 2n个,那么最后的答案就是 ( 2 n ) ! n ! ∗ 2 n \frac{(2n)!}{n!*2^n} n!2n(2n)!

2 n 2n 2n个点的卡特兰数为 f ( n ) f(n) f(n),而且我们发现必须线性求卡特兰数(紫书)。那么最后的答案就是 f ( n ) ∗ n ! ∗ 2 n ( 2 n ) ! \frac{f(n)*n!*2^n}{(2n)!} (2n)!f(n)n!2n

但是此题这样写会MLE,因为 n n n最大为 1 e 7 1e7 1e7,我们需要线性求逆元,求阶乘逆元…空间不够的!怎么办?

那就考虑化简上述公式,使用卡特兰数 C 2 n n C_{2n}^{n} C2nn的公式,然后按组合数定义展开,最后可以化简为 2 n ( n + 1 ) ! \frac{2^n}{(n+1)!} (n+1)!2n

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <math.h>
#include <cstdio>
#include <string>
#include <bitset>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define lowbit(x) (x&(-x))
#define mkp(x,y) make_pair(x,y)
#define mem(a,x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> P;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=2e7+100;

ll quick_mod(ll x,ll n,ll p){
	ll ans=1;
    while(n){
        if(n&1) ans=ans*x%p;
        x=x*x%p;
        n>>=1;
    }
	return ans;
}

ll inv(int a,ll p){
    return quick_mod(a,p-2,p);
}

int main(){
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int n;
    cin>>n;
    ll res=1;
    for(int i=2;i<=n+1;i++)
        res=res*i%Mod;
    ll ans=quick_mod(2,n,Mod)*inv(res,Mod)%Mod;
    cout<<ans<<endl;
    return 0;
}
F-排列计算(树状数组)

这个算是签到题了,比较裸,过的人很多

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <math.h>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const ll INF=1e18;
const int Mod=1e9+7;
const int maxn=2e5+10;
 
ll d[maxn];
int n,m,l,r;
vector<int> ans;
 
void update(int i,int k){
    while(i<=n){
        d[i]+=k;
        i+=lowbit(i);
    }
}
 
ll ask(int i){ //求a[i]
    ll ans=0;
    for(;i;i-=lowbit(i))
        ans+=d[i];
    return ans;
}
 
int main(){
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
 
    cin>>n>>m;
    while(m--){
        cin>>l>>r;
        update(l,1);
        update(r+1,-1);
    }
    for(int i=1;i<=n;i++)
        ans.push_back(ask(i));
    sort(ans.begin(),ans.end(),greater<int>());
    ll res=0;
    for(int i=0,j=n;i<ans.size();i++,j--)
        res+=1LL*j*ans[i];
    cout<<res<<endl;
    return 0;
}
H-时空栈(线段树)

留坑

J-斐波那契和(递推+矩阵快速幂)

因为是线性递推式,看到标答推导过程长篇大论,不如学一手 B M BM BM板子,orz

#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>

using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 998244353;
const int maxn = 2e5 + 10;


#define rep(i,a,n) for (int i=a;i<n;i++)
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
const int N=1e5+10;


ll powMOD(ll a, ll b) {
    ll ans = 1;
    for (; b; b >>= 1, a = a * a%Mod)if (b & 1)ans = ans * a%Mod;
    return ans;
}

namespace linear_seq {
    ll res[N], base[N], _c[N], _md[N];

    vector<int> Md;
    void mul(ll *a, ll *b, int k) {
        rep(i, 0, k + k) _c[i] = 0;
        rep(i, 0, k) if (a[i]) rep(j, 0, k) _c[i + j] = (_c[i + j] + a[i] * b[j]) % Mod;
        for (int i = k + k - 1; i >= k; i--) if (_c[i])
                rep(j, 0, SZ(Md)) _c[i - k + Md[j]] = (_c[i - k + Md[j]] - _c[i] * _md[Md[j]]) % Mod;
        rep(i, 0, k) a[i] = _c[i];
    }
    int solve(ll n, VI a, VI b) {
        ll ans = 0, pnt = 0;
        int k = SZ(a);
        assert(SZ(a) == SZ(b));
        rep(i, 0, k) _md[k - 1 - i] = -a[i]; _md[k] = 1;
        Md.clear();
        rep(i, 0, k) if (_md[i] != 0) Md.push_back(i);
        rep(i, 0, k) res[i] = base[i] = 0;
        res[0] = 1;
        while ((1ll << pnt) <= n) pnt++;
        for (int p = pnt; p >= 0; p--) {
            mul(res, res, k);
            if ((n >> p) & 1) {
                for (int i = k - 1; i >= 0; i--) res[i + 1] = res[i]; res[0] = 0;
                rep(j, 0, SZ(Md)) res[Md[j]] = (res[Md[j]] - res[k] * _md[Md[j]]) % Mod;
            }
        }
        rep(i, 0, k) ans = (ans + res[i] * b[i]) % Mod;
        if (ans < 0) ans += Mod;
        return ans;
    }
    VI BM(VI s) {
        VI C(1, 1), B(1, 1);
        int L = 0, m = 1, b = 1;
        rep(n, 0, SZ(s)) {
            ll d = 0;
            rep(i, 0, L + 1) d = (d + (ll)C[i] * s[n - i]) % Mod;
            if (d == 0) ++m;
            else if (2 * L <= n) {
                VI T = C;
                ll c = Mod - d * powMOD(b, Mod - 2) % Mod;
                while (SZ(C) < SZ(B) + m) C.pb(0);
                rep(i, 0, SZ(B)) C[i + m] = (C[i + m] + c * B[i]) % Mod;
                L = n + 1 - L; B = T; b = d; m = 1;
            }
            else {
                ll c = Mod - d * powMOD(b, Mod - 2) % Mod;
                while (SZ(C) < SZ(B) + m) C.pb(0);
                rep(i, 0, SZ(B)) C[i + m] = (C[i + m] + c * B[i]) % Mod;
                ++m;
            }
        }
        return C;
    }
    int gao(VI& a, ll n) {
        VI c = BM(a);
        c.erase(c.begin());
        rep(i, 0, SZ(c)) c[i] = (Mod - c[i]) % Mod;
        return solve(n, c, VI(a.begin(), a.begin() + SZ(c)));
    }
};

ll f[maxn];

void init(){
    f[1]=f[2]=1;
    for(int i=3;i<N;i++)
        f[i]=(f[i-1]+f[i-2])%Mod;
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    ll n,k;
    init();
    cin>>n>>k;
    vector<int> g(N);
    g[0]=0;
    for(int i=1;i<N;i++)
        g[i]=(g[i-1]+powMOD(i,k)*f[i]%Mod)%Mod;
    int ans=linear_seq::gao(g,n);
    cout<<ans<<"\n";
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值