#10164. 数字游戏 数位dp
f[i][j]表示一共有i位最高位是j时可以满足条件的个数,这个可以直接预处理出来,然后算答案的时候就是算出[0,b]的再减去[0,a]的就可以,主要是如果算出[0,a]这个答案来,先把数的每一位存到一个数组中,然后从高位开始遍历,假设该位的值是now,last代表上一位的数是多少,所以枚举该位的数字的时候范围是last到now-1,如果now小于上一位了说明是有下降的数了直接break,否则让last=now也就是更新last,最后如果走到了最后一位,那说明这个数a也是满足条件的不降数,所以最后答案还要加1
9.106 数字游戏 数位DP——信息学竞赛培训课程_哔哩哔哩_bilibili
#include <bits/stdc++.h>
#define endl '\n'
#define double long double
using namespace std;
const double eps=1e-7;
const double pi=acos(-1);
int f[12][12],w[12];
void init()
{
for(int i=0;i<=9;i++) f[1][i]=1;
for(int i=2;i<=10;i++)
for(int j=0;j<=9;j++)
for(int k=j;k<=9;k++)
f[i][j]+=f[i-1][k];
}
int dp(int n)
{
if(!n) return 1;
int cnt=0;
while(n)
{
w[++cnt]=n%10;
n/=10;
}
int res=0,last=0;
for(int i=cnt;i>=1;i--)
{
int now=w[i];
for(int j=last;j<now;j++)
res+=f[i][j];
if(now<last) break;
last=now;
if(i==1) res++;
}
return res;
}
int main()
{
// cin.tie(0);
// cout.tie(0);
// ios::sync_with_stdio(0);
int a,b;
init();
while(cin>>a>>b)
{
cout<<dp(b)-dp(a-1)<<endl;
}
system("pause");
return 0;
}
P2657 [SCOI2009] windy 数 数位dp
还是和上一个题目数字游戏一样,f[i][j]表示一共有i位最高位是j的个数,但是这个是不能包含前导零的, 预处理还是和之前差不多的,dp函数就需要注意一下,因为不含前导零了,所以位数不都相同了,上一题像是在找字符串,这一题是在找数字,所以这个题的那层循环只能判断最高位的数字,所以之后还要把小于cnt位的给统计上
9.107 Windy数 数位DP——信息学竞赛培训课程_哔哩哔哩_bilibili
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
#define double long double
using namespace std;
const int mod=1e8;
const double eps=1e-7;
const double pi=acos(-1);
int f[15][15];
void init()
{
for(int i=0;i<10;i++) f[1][i]=1;
for(int i=2;i<=11;i++)
for(int j=0;j<=9;j++)
for(int k=0;k<=9;k++)
if(abs(j-k)>=2) f[i][j]+=f[i-1][k];
}
int dp(int n)
{
if(n==0) return 0;
int last=-1,res=0;
int a[11],cnt=0;
while(n)
{
a[++cnt]=n%10;n/=10;
}
for(int i=cnt;i>=1;i--)
{
int now=a[i];
for(int j=(i==cnt);j<now;j++)
if(abs(j-last)>=2) res+=f[i][j];
if(abs(now-last)<2) break;
last=now;
if(i==1) res++;
}
for(int i=1;i<cnt;i++)
for(int j=1;j<=9;j++)
res+=f[i][j];
return res;
}
signed main()
{
// cin.tie(0);
// cout.tie(0);
// ios::sync_with_stdio(0);
int a,b;
init();
cin>>a>>b;
cout<<dp(b)-dp(a-1)<<endl;
system("pause");
return 0;
}
D - Caesar's Legions dp
dp[0][i][j][k]表示前i个位置一共放了j个1连续放了k个1的方案数,dp[1][i][j][k]表示前i个位置一共放了j个2连续放了k个2的方案数;
则转移就比较好想了,考虑第i个位置放1的情况,假设第i-1个位置也是1,那么转移方程就是dp[0][i][j][k]+=dp[0][i-1][j-1][k-1],如果第i-1个位置是2,那么这个位置开始连续的1的个数就要从1重新开始了,即dp[0][i][j][1]+=dp[1][i-1][i-j][1~lim[2]],i-j表示前i-1个位置一共用了多少2,因为这个重新开始的1前面2的个数是不确定的,所以是所有的情况都加起来,即1~lim[2],lim[2]也就是题目中的k2;
最后统计答案也就是考虑最后一个是1还是2,然后枚举连续出现了几次全加起来就可以
#include <bits/stdc++.h>
#define endl '\n'
#define double long double
using namespace std;
const int mod=1e8;
const double eps=1e-7;
const double pi=acos(-1);
int n[2],lim[2];
int dp[2][205][205][22];
int main()
{
// cin.tie(0);
// cout.tie(0);
// ios::sync_with_stdio(0);
cin>>n[0]>>n[1]>>lim[0]>>lim[1];
memset(dp,0,sizeof(dp));
dp[0][1][1][1]=dp[1][1][1][1]=dp[0][0][0][0]=dp[1][0][0][0]=1;
for(int i=2;i<=n[0]+n[1];i++)
{
for(int t=0;t<2;t++)
{
for(int j=1;j<=min(i,n[t]);j++)
{
int tmp=0;
for(int k=1;k<=min(i-j,lim[!t]);k++)
{
tmp=(tmp+dp[!t][i-1][i-j][k])%mod;
}
dp[t][i][j][1]=(dp[t][i][j][1]+tmp)%mod;
for(int k=2;k<=min(j,lim[t]);k++)
{
dp[t][i][j][k]=(dp[t][i][j][k]+dp[t][i-1][j-1][k-1])%mod;
}
}
}
}
int ans=0;
for(int t=0;t<2;t++)
for(int k=1;k<=lim[t];k++)
ans=(ans+dp[t][n[1]+n[0]][n[t]][k])%mod;
cout<<ans<<endl;
system("pause");
return 0;
}
478C - Table Decorations 思维
这题其实自己多想想也是可以想出来的,,还是懒了,,
先给三个数排序,如果a[3]>=2*(a[1]+a[2])那么可以让两个a[3]对应每个a[1]或a[2];否则总有办法可以让最大值等于(a[1]+a[2]+a[3])/3,也就是说经过组合后剩下的个数一定是小于3的
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
#define double long double
using namespace std;
const int mod=1e8;
const double eps=1e-7;
const double pi=acos(-1);
int a[3];
signed main()
{
// cin.tie(0);
// cout.tie(0);
// ios::sync_with_stdio(0);
cin>>a[0]>>a[1]>>a[2];
sort(a,a+3);
if((a[0]+a[1])*2>a[2]) cout<<(a[0]+a[1]+a[2])/3<<endl;
else cout<<(a[0]+a[1])<<endl;
system("pause");
return 0;
}
126B - Password 字符串哈希
一开始是wa,最后看清楚条件后就疯狂的在T,最后再看了遍题目,发现其实每次要寻找的字符串的起点都是一样的,都是s[1],那么就把s[1]出现的坐标都记录下来,然后第一层循环倒着枚举s[1]的坐标,也就是在枚举后缀,然后看看是否和前缀相同,如果相同就正着再去枚举s[1]的坐标,看看中间段是否和前缀相同,如果相同就更新ans的值,虽然还是两层循环但是没想到奇迹般的过了,并且只用了154ms,叼哉~
#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
using namespace std;
const int mod=1e9+7;
int qpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int getinv(int x){return qpow(x,mod-2);}
const int base=133331;
int h[1000006],p[1000006];
int gethash(int x,int y)
{
return ((h[y]-h[x-1]*p[y-x+1]%mod)%mod+mod)%mod;
}
char s[1000006];
vector<int>v;
signed main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
cin>>s+1;
int n=strlen(s+1);
h[0]=0;p[0]=1;
for(int i=1;i<=n;i++)
{
h[i]=(h[i-1]*base%mod+s[i]-'a'+1)%mod;
p[i]=p[i-1]*base%mod;
}
for(int i=2;i<=n;i++)
{
if(s[i]==s[1]) v.push_back(i);
}
int ans=0;
for(int i=v.size()-1;i>=0;i--)
{
int len=n-v[i]+1;
int h1=gethash(1,len);
int h2=gethash(v[i],n);
if(h1==h2)
{
for(int j=0;j<v.size();j++)
{
if(v[j]>=v[i]) break;
int h3=gethash(v[j],v[j]+len-1);
if(h3==h1)
{
ans=len;break;
}
}
}
}
if(ans)
{
for(int i=1;i<=ans;i++) cout<<s[i];
cout<<endl;
}
else cout<<"Just a legend"<<endl;
system("pause");
return 0;
}
1328D - Carousel 思维
这题有点徒有虚名了,感觉放在B题都不为过,可以看出最多三种颜色就够了,如果只用一种类型的话就都涂成1种颜色,否则就奇数填2偶数填1,如果ans[n]==ans[1]&&a[n]!=a[1]了,那就看看a数组中有没有类型相同且相邻的元素,如果有就让后一个等于前一个的颜色,这样后面的奇偶性就会改变了,ans[n]也就不会等于ans[1]了,如果没有的话就只能添加一种颜色了;
#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
using namespace std;
const int mod=1e9+7;
int qpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
int getinv(int x){return qpow(x,mod-2);}
int q,n,a[200005],ans[200005];
signed main()
{
// cin.tie(0);
// cout.tie(0);
// ios::sync_with_stdio(0);
cin>>q;
while(q--)
{
cin>>n;
a[0]=0;
int cnt=2,ma=0;
for(int i=1;i<=n;i++) cin>>a[i];
int flag=1;
for(int i=2;i<=n;i++)
{
if(a[i]!=a[i-1]){flag=0;break;}
}
if(flag)
{
cout<<1<<endl;
for(int i=1;i<=n;i++) cout<<1<<" ";
cout<<endl;
continue;
}
for(int i=1;i<=n;i++)
{
ans[i]=i%2;
if(a[i]==a[i-1]) ma=i;
}
if(ans[1]==ans[n]&&a[1]!=a[n])
{
if(ma)
{
//cout<<ans[1]<<" sss "<<ans[n]<<" "<<n<<" "<<ma<<" "<<a[ma]<<" "<<a[ma-1]<<endl;
ans[ma]=ans[ma-1];
for(int i=ma+1;i<=n;i++)
ans[i]=(i+1)%2;
}
else cnt++,ans[n]=2;
}
cout<<cnt<<endl;
for(int i=1;i<=n;i++) cout<<ans[i]+1<<" ";cout<<endl;
}
system("pause");
return 0;
}
K - Triangle 计算几何
在网上找的题解交到cf上都超时了,但在计蒜客上是通过的,感觉再弄下去也不是很有意义就得过且过了,,,
其实思路还是比较简单的,面积是知道的,底也是知道的,直接算出高然后找一个在直线上的点就可以,但是实现起来太麻烦了,而且斜率还有可能不存在,需要讨论的情况就多了起来,这是头一次没有循环光if else就可以超时的题目,,,
斜率太麻烦,可以转化成向量来求解
通过面积公式就可以列出下面的等式
然后化简后就可得到
之后就可以根据BA的方向来确定Q的坐标了
#include <bits/stdc++.h>
#define int long long
//#define double long double
#define ios cin.tie(0), cout.tie(0), ios::sync_with_stdio(0)
#define endl '\n'
using namespace std;
const int N = 1e3 + 100;
const int mod = 1e9 + 7;
const double eps=1e-8;
int sgn(double x)
{
if(fabs(x)<eps) return 0;
return x<0?-1:1;
}
struct Point
{
double x,y;
Point(){}
Point(double x,double y):x(x),y(y){}
Point operator - (Point B)
{
return Point(x-B.x,y-B.y);
}
}P[5],pos[5];
struct Line
{
Point p1,p2;
Line(){}
Line(Point p1,Point p2):p1(p1),p2(p2){}
}L[5];
double Dist(Point A,Point B)//两点的距离
{
return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}
double Dot(Point A,Point B)//向量点乘
{
return A.x*B.x+A.y+B.y;
}
double Cross(Point A,Point B)//向量叉乘
{
return A.x*B.y-A.y*B.x;
}
bool Point_on_seg(Point p,Line v)//判断点是否在直线上
{
return sgn(Cross(p-v.p1,v.p1-v.p2))==0&&sgn(Dot(p-v.p1,p-v.p2))<=0;
//叉乘等于0说明斜率相等在一条直线上
//点乘小于等于0说明p,v.p1与p,v.p2的方向不同,说明p在这条线段上
}
signed main()
{
//ios;
//freopen("in.txt","r",stdin);
int t;
cin>>t;
while(t--)
{
for(int i=1;i<=4;i++) cin>>P[i].x>>P[i].y;
L[1]=Line(P[1],P[2]);
L[2]=Line(P[2],P[3]);
L[3]=Line(P[1],P[3]);
if(Point_on_seg(P[4],L[1])==0&&Point_on_seg(P[4],L[2])==0&&Point_on_seg(P[4],L[3])==0)
{
cout<<"-1\n";
continue;
}
int p=0;
for(int i=1;i<=3;i++)
{
if(Point_on_seg(P[4],L[i])==1)
{
p=i;break;
}
}
if(p==1) pos[1]=P[3];
else if(p==2) pos[1]=P[1];
else if(p==3) pos[1]=P[2];
if(Dist(P[4],L[p].p1)>Dist(P[4],L[p].p2))
{
pos[2]=L[p].p1;
pos[3]=L[p].p2;
}
else pos[2]=L[p].p2,pos[3]=L[p].p1;
double cnt=Dist(P[4],pos[2])/Dist(pos[2],pos[3]);
cnt=0.5/cnt;
double ansx=cnt*(pos[1].x-pos[2].x)+pos[2].x;//这个地方不是很明白,但应该是可以分开算坐标的
double ansy=cnt*(pos[1].y-pos[2].y)+pos[2].y;
cout<<fixed<<setprecision(12)<<ansx<<" "<<ansy<<endl;
}
system("pause");
return 0;
}