Problem - B - Codeforces
思路:
- 数位dp,如果我们暴力的计算的状态的话,显然就是记录每个数字出现几次。但是显然这样难以发挥数位dp的记忆化功效,因为只有出现次数相同,你是什么数字,实际是无所谓的。所以我们尝试记录每个出现次数有多少个数字
- 尝试打表发现,结果只有1477种
-
int ans; void dfs(int i,int cnt,int sum)//i为出现i次,cnt录入几个不同的数字,sum为选的i的和 { if(sum>18)return; if(cnt>10)return; if(i==19) { ans++; return; } for(int j=0; j<=18/i; ++j)//j为出现i次有j个数 { dfs(i+1,cnt+j,sum+j*i); } } void mysolve() { dfs(1,0,0); cout<<ans<<endl; }
- 所以,对于每个数字出现的状态,我们可以用每个出现次数有几个数字这个状态来记录
- 接下来就是普通的数位dp(记得处理前导0问题)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl "\n"
#define int ll
const int N = 20;
int dp[N][2000],bit[N];
map<vector<int>,int>mp;
int cnt;
//状态打表
//int ans;
//
//void dfs(int i,int cnt,int sum)//i为出现i次,cnt录入几个不同的数字,sum为选的i的和
//{
// if(sum>18)return;
// if(cnt>10)return;
// if(i==19)
// {
// ans++;
// return;
// }
// for(int j=0; j<=18/i; ++j)//j为出现i次有j个数
// {
// dfs(i+1,cnt+j,sum+j*i);
// }
//}
//
//void mysolve()
//{
// dfs(1,0,0);
// cout<<ans<<endl;
//}
int dfs(int len,vector<int>a,bool limit,bool zero)
{
if(!len)return *max_element(a.begin(),a.end());
vector<int>p(20);
for(auto v:a)if(v)p[v]++;//将a数组状态转为每个出现次数有几个数字的状态,用map存储对应的数组
if(!mp[p])mp[p]=++cnt;
int sta=mp[p];
if(!limit&&dp[len][sta]!=-1)return dp[len][sta];
int ans=0;
int top=limit?bit[len]:9;
for(int i=0; i<=top; ++i)
{
vector<int>tmp=a;
if(i||len==1||!zero)tmp[i]++;//zero判断前导零问题
ans+=dfs(len-1,tmp,limit&&i==top,(!i)&&zero);
}
if(!limit)return dp[len][sta]=ans;//存储无限制下的状态
return ans;
}
int cal(int x)//把数字x按位存储到数组
{
if(x<0)return 0;
if(x==0)return 1;//可能要特判0与负数的贡献
int len=0;
while(x)bit[++len]=x%10,x/=10;
return dfs(len,vector<int>(10),1,1);
}
void mysolve()
{
int l,r;
cin>>l>>r;
cout<<cal(r)-cal(l-1)<<endl;
}
int32_t main()
{
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);//使用read请把解绑注释了
int t=1;
cin >> t;
//read(t);
memset(dp,-1,sizeof(dp));
while (t--)
{
mysolve();
}
system("pause");
return 0;
}
Problem - I - Codeforces
思路:
- 显然首先答案可以转化为求取最小的x使得x*(x+1)%(lcm*2)==0
- 容易发现x与x+1互质,又因为lcm可以分解为其质因子的乘积(形如lcm=p1^n1*p2^n2...pi^ni)
- 因为x与x+1互质,显然我们他们不能拥有相同的lcm的因子
- 那么问题转化为,lcm把因子分为a,b两部分(显然要求a,b互质,不拥有相同的因子),问是否存在ax+1=by(即原来的x+1=x+1)。(x,y为未知数)
- 这个形式观察出实际是exgcd(-ax+by=1),所以答案转化为,枚举各个因子的组合a与b,求a的逆元x,那么-ax就是我们要求的(x*(x+1)%(lcm*2)==0)。我们求出最小的-ax
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl "\n"
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 10;
const int mod = 998244353;
bool vis[N];
int ou[N];
void oula(int n)
{
for(int i=2; i<=n; ++i)
{
if (!vis[i])
{
vis[i] = 1;
ou[++ou[0]] = i;//ou[0]是目前素数个数
}
for (int j = 1; j <= ou[0] && 1ll*i*ou[j]<= n; ++j)
{
vis[i * ou[j]] = 1;//每个数由其最大质因子筛去
if (i % ou[j] == 0)break;//避免重复筛去(i*ou[j+1]=k*ou[j]*ou[j+1]=t*ou[j+1]),后面自然会出现t帮忙筛去这个t*ou[j+1]
}
}
}
void exgcd(ll a, ll b, ll &x, ll &y)//&直接修改值
{
if (!b)x = 1, y = 0;
else
{
exgcd(b, a % b, y, x);//x继承了深层的y,y就继承深层的x,y再减去(a/b)*y2即-(a/b)*x即可
y -= (a / b) * x;
}
}
vector<int>v;
ll ans=INF;
void dfs(int i,ll res,ll sum)//dfs暴力枚举因子组合
{
if(i==(int)v.size())
{
ll a=res,b=sum/res,x,y;
exgcd(a, b, x, y);
x = ( -x + b) % b;//保证逆元为正数,这里x取的是-x(-ax),但是要求-ax>0
if(x)ans=min(ans,x*a);
else ans=min(ans,sum);//特判res=1,此时模数为1,x必为0
return;
}
dfs(i+1,res*v[i],sum*v[i]);
dfs(i+1,res,sum*v[i]);
}
void mysolve()
{
oula(1e4);
int n,x;
cin>>n;
map<int,int>mp;
for(int i=1; i<=n; ++i)
{
cin>>x;
for(int i=1; i<=ou[0]&&1ll*ou[i]<=x; ++i)
{
if(x%ou[i]==0)
{
int cnt=0;
while(x%ou[i]==0)cnt++,x/=ou[i];
mp[ou[i]]=max(mp[ou[i]],cnt);
}
}
if(x>1)
{
mp[x]=max(mp[x],1);
}
}
mp[2]++;
for(auto [k,val]:mp)//求出lcm的因子
{
int res=1;
for(int i=1; i<=val; ++i)res=1ll*res*k;
v.push_back(res);
}
dfs(0,1,1);
cout<<ans<<endl;
}
int32_t main()
{
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);//使用read请把解绑注释了
int t=1;
//cin >> t;
//read(t);
while (t--)
{
mysolve();
}
system("pause");
return 0;
}
Problem - J - Codeforces
思路:
- 我们可以贪心的前往前方资源更大的,显然资源最大的留最久最好,所以每个点都是尽可能快的前往下一个比当前资源大的点
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl "\n"
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define int long long
typedef pair<int, int> pii;
inline int read(int &x);
//double 型memset最大127,最小128
//---------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------//
const int N = 3e5 + 10;
const int mod = 998244353;
int a[N],b[N];
ll sum[N];
void mysolve()
{
int n;
cin>>n;
for(int i=1; i<=n; ++i)cin>>a[i],sum[i]=a[i]+sum[i-1];
if(sum[n]<0||sum[1]<0)
{
cout<<-1<<endl;
return;
}
int ans=n,res=sum[1];//首先,每个都是要经过一次,答案>=n
ll mx=sum[1];//枚举当前使用的资源最大点
for(int i=1; i<n; ++i)
{
mx=max(sum[i],mx);
if(res+sum[i+1]<0)
{
if(mx<=0)
{
cout<<-1<<endl;
return;
}
int cnt=(-(res+sum[i+1])+mx-1)/mx;
ans+=cnt;
res+=sum[i+1]+cnt*mx;
}
else res+=sum[i+1];
}
cout<<ans<<endl;
}
int32_t main()
{
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);//使用read请把解绑注释了
int t=1;
//cin >> t;
//read(t);
while (t--)
{
mysolve();
}
system("pause");
return 0;
}
Problem - K - Codeforces
思路:
- 观察出,打败n个人与一个一个人打败是相互独立的,且状态相同,即每个人都是独立且相同的
- 所以我们只需要讨论跟一个人打输的概率,那么答案就是n方
- 假设当前n+1人的骰子固定是k,那么他第一局输的概率是(m-k)/m,第二局是(m-k)/(m*m)...第n局是(m-k)/(m^n),合并得到(等比求和)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl "\n"
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define int long long
typedef pair<int, int> pii;
inline int read(int &x);
//double 型memset最大127,最小128
//---------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------//
const int N = 3e5 + 10;
const int mod = 998244353;
ll fastmi(ll base, ll power)
{
ll ans = 1;
while (power)
{
if (power & 1)ans=ans*base%mod;
base=base*base%mod;
power >>=1;
}
return ans;
}
void mysolve()
{
int n,m;
cin>>n>>m;
int inv=fastmi(m-1,mod-2);
for(int i=1; i<=m; ++i)
{
int res=(m-i)*inv%mod;
cout<<fastmi(res,n)<<" ";
}
}
int32_t main()
{
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);//使用read请把解绑注释了
int t=1;
//cin >> t;
//read(t);
while (t--)
{
mysolve();
}
system("pause");
return 0;
}
inline int read(int &x)
{
x = 0;
char ch = 0;
while (ch < '0' || ch > '9')ch = getchar();
while (ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x;
}