数位dp,2019 K - Triangle 计算几何

142 篇文章 1 订阅
92 篇文章 0 订阅

#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;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

killer_queen4804

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值