A-张老师和菜哭武的游戏(裴蜀定理)
比赛时光顾着找规律了,因为我发现只要首先选的数有一个是奇数那么就能选到区间 [ 1 , n ] [1,n] [1,n]的所有数,否则就只能选区间内的所有偶数。暴力枚举了样例和自己写的例子,也都对,真是没辙…怪自己没想到去手推一下公式。也可能是只记得exgcd忘记了裴蜀定理?
首先假设选出的两个数为 x 、 y x、y x、y并且假设 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=x−y加入
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=2x−y
…
那么我们不难发现,实际上除了一开始选择的两个数,后面的数 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;
}