B. Flipping Game
题意:
给一个n大小的01串,可以进行k次操作,0代表关灯,1代表开灯,每次操作是对m个灯进行取反,问最后一次操作后可以成为目标的串,这个方案有多少个?
思路: DP,我们考虑$f[i][j]$表示进行i次操作,有j个灯与目标状态不同的方案数,当我们求这一层的$f[i][j]$的方案数时,它是由上一层转移过来的,我们枚举l,代表第i次操作时我们改变了l个与目标状态不同的灯然后由上一层的j状态转移到了当前层的g状态,$g=j-l+(m-l)$,当前层就是$f[i][j+m-2*l]$那么我们就要保证$j+m- 2 * l>=0$, 并且$n-j>=m-l$,然后我们操作的m个灯,有l个是不等变为等,有m-l个是等变为不等,那么我们在j里面取l个,在n-j里面取m-l个,就可以转移:
状态转移方程式:
$ f[i][j]=(f[i][j]+f[i-1][j+m-2 * l] * C(j)l * C(n-j)(m-1))$,别忘了取模。
细节:组合数预处理100以内的,不然直接求的话会超时,卢卡斯求,暴力求都可以,初始化为f[0][s]=0,s代表出状态不等的个数,答案即是f[k][0]。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define LL long long
#define pii pair<int,int>
struct node{
int l,r;
}a[100010];
bool cmp(node x,node y){
return x.r<y.r;
}
const int mod=998244353;
//若p是质数,则对于任意整数 1 <= m <= n,有:
//C(n, m) = C(n % p, m % p) * C(n / p, m / p) (mod p)
map<pair<int,int>,int>mp;
int qmi(int a, int k, int p) { // 快速幂模板
int res = 1 % p;
while (k) {
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int C(int a, int b, int p) { // 通过定理求组合数C(a, b)
if (a < b) return 0;
LL x = 1, y = 1; // x是分子,y是分母
for (int i = a, j = 1; j <= b; i --, j ++ ) {
x = (LL)x * i % p;
y = (LL) y * j % p;
}
return x * (LL)qmi(y, p - 2, p) % p;
}
int lucas(LL a, LL b, int p) {
if (a < p && b < p) return C(a, b, p);
return (LL)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
void solve(){
int n,m,k;
cin>>n>>k>>m;
string s1,s2;
cin>>s1>>s2;
int s=0;
for (int i = 0; i <s2.size() ; ++i) {
if(s2[i]!=s1[i])s++;
}
vector<vector<int>>f(k+5,vector<int>(n+5));
f[0][s]=1;
for (int i = 1; i <=k ; ++i) {
for (int j = 0; j <=n ; ++j) {
if(f[i-1][j]==0)continue;
for (int l = 0; l <=m ; ++l) {
if(j>=2*l-m&&n-j>=m-l){
// f[i][j+m-2*l]=(f[i][j+m-2*l]+(C(j,l,mod)%mod*C(n-j,m-l,mod)*f[i-1][j])%mod)%mod;
f[i][j+m-2*l]=(f[i][j+m-2*l]%mod+(mp[{j,l}]%mod*mp[{n-j,m-l}]%mod*f[i-1][j])%mod)%mod;
}
}
}
}
// for (int i = 1; i <=k ; ++i) {
// cout<<i<<':';
// for (int j = 0; j <=n; ++j) {
// cout<<f[i][j]<<' ';
// }
// cout<<endl;
// }
cout<<f[k][0]%mod<<'\n';
}
signed main(){
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int t;
cin>>t;
for (int i = 0; i <=100 ; ++i) {
for (int j = 0; j <=i ; ++j) {
mp[{i,j}]= C(i,j,mod);
}
}
while (t--){
solve();
}
}
F. Stones in the Bucket
题意:地面上有n个水桶,其中第i个水桶装有ai块石头。每次可以进行以下两种操作之一:
- 从一个非空桶中移走一块石头。
- 从其中一个桶(必须是非空的)中移动一块石头到其他任何一个桶(可以是空的)。
- 要使所有水桶中的石子数量相同,最少需要进行多少次操作?
思路:求一下最终的平均值,然后遍历一遍,当小于平均值,则进行操作二,最后再进行操作一,抹去多的
代码:
#include<bits/stdc++.h> using namespace std; #define int long long #define pii pair<int,int> void solve(){ int n; cin>>n; vector<int >q(n+1); int s=0; for (int i = 1; i <=n ; ++i) { cin>>q[i]; s+=q[i]; } int sum=0; int d=s%n; int dd=s/n; for(int i=1;i<=n;i++){ if(q[i]<dd){ sum+=dd-q[i]; } } sum+=d; cout<<sum<<endl; } signed main(){ ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr); int t; cin>>t; while (t--){ solve(); } }
L. Median
题意:一个数组是n大小的,给m条线索,x-y代表a[y]大于a[x],问满足这m条线索能否有一个中位数存在,中位数的位置标为1,其他位置为0;
思路:数据范围较小,直接正向建边,反向建边,然后枚举起点,从起点跑到终点,如果不存在环,且正反跑到的数字都小于等于n/2,那么这个位置就可以作为中位数
代码:
#include<bits/stdc++.h> using namespace std; //#define int long long #define LL long long #define pii pair<int,int> struct node{ int l,r; }a[100010]; bool cmp(node x,node y){ return x.r<y.r; } const int mod=998244353; void solve(){ int n,m; cin>>n>>m; vector<int>mp1[n+1],mp2[n+1]; for (int i = 0; i <m ; ++i) { int x,y; cin>>x>>y; mp1[x].push_back(y); mp2[y].push_back(x); } auto bfs1=[&](int s){ set<int> stt; stt.insert(s); queue<int >q; q.push(s); while (q.size()){ auto t=q.front(); q.pop(); for (auto i:mp1[t]) { if(i==s){ return n; } else if(stt.count(i)==0){ stt.insert(i); q.push(i); } } } return (int)stt.size()-1; }; auto bfs2=[&](int s){ set<int> stt; stt.insert(s); queue<int >q; q.push(s); while (q.size()){ auto t=q.front(); q.pop(); for (auto i:mp2[t]) { if(i==s){ return n; } else if(stt.count(i)==0){ stt.insert(i); q.push(i); } } } return (int)stt.size()-1; }; int p=-1; vector<int>res; int da; int xiao; for (int i = 1; i <=n ; ++i) { da=bfs1(i); xiao=bfs2(i); if(da==n||xiao==n)break; if(da<=n/2&&xiao<=n/2){ res.push_back(1); } else res.push_back(0); } if(da==n||xiao==n){ for (int i = 0; i <n ; ++i) { cout<<'0'; } cout<<'\n'; } else{ for (auto i:res) { cout<<i; } cout<<'\n'; } } signed main(){ ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr); int t; cin>>t; while (t--){ solve(); } }
B. Grid with Arrows
题意:给一个n * m的图,给出每个点的下一步的方向,然后给出走的步长是多少,问有没有一种可能在某点出发,可以遍历到全图
思路:两种情况,一种是这个图是个环,一种是一条链,局部有环。
- 第一种情况直接随便选个点跑一遍图,如果能够跑n * m个点,那么就yes
-
第二种情况是从入度为0的点开始跑,跑完全图就是yes,其他是no
diamond:
#include<bits/stdc++.h> using namespace std; #define int long long const int mod=1e9+7; int fa[100005]; int n,m; //int find(int x){ // if(fa[x]!=x){ // fa[x]=find(fa[x]); // } // return fa[x]; //} //void merge(int x,int y){ // int dx=find(x),dy=find(y); // fa[dy]=dx; //} int get(int x,int y){ return (int)x*m+y; } void solve(){ int cnt=0; cin>>n>>m; // vector<int>vis(n*m); vector<int>g[(n+1)*(m+1)]; vector<string>s(n+4); for (int i = 0; i <n ; ++i) { cin>>s[i]; } vector<int>du(n*m+5,0); int dian; int st=0; for (int i = 0; i <n ; ++i) { for (int j = 0; j <m ; ++j) { int q; cin>>q; int x=i,y=j; int xx = 0,yy=0; if(s[x][y]=='r'){ yy=y+q; xx=x; } else if(s[x][y]=='l'){ yy=y-q; xx=x; } else if(s[x][y]=='u'){ xx=x-q; yy=y; } else if(s[x][y]=='d'){ xx=x+q; yy=y; } int a= get(x,y); int b= get(xx,yy); // cout<<xx<<' '<<yy<<endl; if(xx<0||xx>=n||yy<0||yy>=m){ continue; } else{ g[a].push_back(b); du[b]++; } } } dian=-1; for (int i = 0; i <n*m ; ++i) { if(du[i]==0){ dian=i; break; } } // cout<<dian<<endl; if(dian==-1){ dian=0; } vector<int>vis(n*m+5); function< void (int )> dfs2= [&](int u){ vis[u]=1; for (auto i:g[u]) { if(!vis[i]){ cnt++; dfs2(i); } } }; cnt=1; vis[dian]=1; dfs2(dian); if(cnt==n*m){ cout<<"Yes\n"; } else{ cout<<"No\n"; } } signed main(){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); int t; cin>>t; while (t--){ solve(); } }
C.0689
题意:
一个字符串,仅有0、6、8、9组成,选取一个子串,翻转,得到新的字符串,问新得到的字符串不同的有多少个?0,8翻转还是自己,6翻转是9,9翻转是6。
思路:
如果每个数都不一样,那么翻转后不同的就应该是所有子串和自己,即(n+1) * n/2+1,我们把重复的减去,
去重,如果开头-末尾是0 - 0,8 -8 ,6 - 9,9 - 6,这些都是重复的比如0680,翻转0680,其实是翻转68,和开头结尾没关系,0 - 0的情况,是统计出0的个数,那么以0结尾的子串的个数就是(1+num) * num/2; 8-8也一样,
9-6和6-9的情况,统计出9的个数x,6的个数y,那么重复个数是x * y。
还有一个特例,如果全为6或者全为9,翻转后是必然得不到自己原来的状态的,那么ans就--;
diamond:
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
int mod;
int n,p;
void solve() {
string s;
cin>>s;
int n=s.size();
vector<int>num(4,0);
for (int i = 0; i <n ; ++i) {
if(s[i]=='0')num[0]++;
else if(s[i]=='6')num[1]++;
else if(s[i]=='8')num[2]++;
else num[3]++;
}
int ans=(n+1)*n/2+1;
ans-=(num[0]+1)*num[0]/2;
ans-=(num[2]+1)*num[2]/2;
ans-=num[1]*num[3];
if(num[1]==n||num[3]==n)ans--;
cout<<ans<<'\n';
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t=1;
cin>>t;//
while (t--){
solve();
}
}