【计算几何】【数位DP】【tarjan】Day 10.6

T1 计算几何+递推

 1 #include <cstdio>
 2 #include <cmath>
 3 double w,x,r;
 4 int p;
 5 int main()
 6 {
 7     freopen("coin.in","r",stdin);
 8     freopen("coin.out","w",stdout);
 9     scanf("%lf%d",&r,&p);
10     w=r*sqrt(3.0)/3;
11     x=w*2-r;
12     w-=x;
13     for (int i=2;i<=p;i++)
14     {
15         x=pow(w,2.0)/(2*(w+r));
16         w-=2*x;
17     }
18     printf("%.6lf",x);
19 }

T2 数位DP

转移动作:选接下来的数

那么如何依据转移动作建立转移方程?

考虑:每一位的数受上一位所选择的数所影响,还受已选择的前缀和原数对应的前缀的大小关系影响,所以定义状态的时候需要考虑到这点

再仔细一想,如果已选择的前缀小于原数对应的前缀,那么当前位可以随便选;如果已选择的前缀等于原数对应的前缀,那么当前位只能小于原数对应的当前位

所以说我们还需要一个0/1下标来描述这两种特征吗?

不是的,注意到如果已选择的前缀等于原数对应的前缀,那么对应的方案数只有0和1两种取值,而且取值只和原数对应的前缀是否合法有关,所以只需要在DP的同时维护pd变量来标记原数对应的前缀是否合法即可

定义f[i][j]为已经选择了前i位且第i位选择了j且已选择的前缀小于原数对应的前缀的幸运数数量

最后再加上pd就是已经选择了前i位且第i位选择了j且已选择的前缀小于等于原数对应的前缀的幸运数数量

想一想:为什么不定义f[i][j]]为已经选择了前i位且第i位选择了j且已选择的前缀小于等于原数对应的前缀的幸运数数量?

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <cmath> 
 5 #define ll long long
 6 using namespace std;
 7 ll l,r,f[19][10],w[19],ans=0,ten[19]={1};
 8 ll count(ll x)
 9 {
10     ll s=0;
11     while(x)
12     {
13         s++;
14         w[s]=x%10;
15         x/=10;
16     }
17     return s;
18 }
19 ll check(ll x)//统计小于等于x且和x位数相同的幸运数数量 
20 {
21     memset(f,0,sizeof(f));
22     ll cnt=count(x),s=0,pd=1;
23     if (cnt==0) return 0;
24     for (int i=1;i<w[cnt];i++) f[cnt][i]=1;
25     for (int i=cnt-1;i>=1;i--)//统计小于x且和x位数相同的幸运数数量 
26     {
27         for (int j=0;j<=9;j++)//枚举当前位选的数 
28         {
29             for (int k=0;k<=9;k++)//枚举上一位选的数 
30             {
31                 if (abs(j-k)>=2)//判断转移是否合法 
32                 {
33                     if (k==w[i+1]&&j<w[i]) f[i][j]+=pd;//从前缀完全相同的状态转移(前提是第i+1位及之前的前缀合法) 
34                     f[i][j]+=f[i+1][k];//从比原数前缀小的状态转移
35                 }
36             }
37         }
38         if (abs(w[i]-w[i+1])<2) pd=0;//判定第i位及之前的前缀合法 
39     }
40     for (int i=0;i<=9;i++) s+=f[1][i];//统计比x小的幸运数 
41     s+=pd;//统计x本身是不是幸运数 
42     return s;
43 }
44 int main()
45 {
46     freopen("lucky.in","r",stdin);
47     freopen("lucky.out","w",stdout);
48     cin>>l>>r;
49     for (int i=1;i<=17;i++) ten[i]=ten[i-1]*10;
50     for (int i=count(l);i<=count(r)-1;i++) ans+=check(ten[i]-1);
51     ans+=check(r)-check(l-1);
52     cout<<ans<<endl;
53 }

 T3 tarjan求割点

 1 #include <cstdio>
 2 #include <iostream>
 3 using namespace std;
 4 struct E{
 5     int next,to;
 6 }e[400001];
 7 int n,m,a,b,ans=0,sz=0,head[100001],du[100001],dfn[100001],low[100001],gd[100001],son[100001],cnt=0;
 8 void insert(int a,int b)
 9 {
10     sz++;
11     e[sz].next=head[a];
12     head[a]=sz;
13     e[sz].to=b;
14 }
15 void tarjan(int x,int fa)
16 {
17     low[x]=dfn[x]=++cnt;
18     for (int i=head[x];i;i=e[i].next)
19         if (e[i].to!=fa)
20         {
21             if (!dfn[e[i].to])
22             {
23                 son[e[i].to]++;
24                 if (son[e[i].to]>1) gd[x]=1;
25                 tarjan(e[i].to,x);
26                 if (low[e[i].to]>=dfn[e[i].to]&&x!=1) gd[x]=1;
27                 low[x]=min(low[x],low[e[i].to]);
28             }
29             else low[x]=min(low[x],dfn[e[i].to]);
30         }
31 }
32 int main()
33 {
34     freopen("kingdom.in","r",stdin);
35     freopen("kingdom.out","w",stdout);
36     scanf("%d%d",&n,&m);
37     for (int i=1;i<=m;i++)
38     {
39         scanf("%d%d",&a,&b);
40         insert(a,b);insert(b,a);
41         du[a]++;du[b]++;
42     }
43     tarjan(1,1);
44     for (int i=1;i<=n;i++)
45         if (!gd[i]&&m-du[i]==n-2) ans++;
46     printf("%d\n",ans);
47     for (int i=1;i<=n;i++)
48         if (!gd[i]&&m-du[i]==n-2) printf("%d ",i);
49 }

 

转载于:https://www.cnblogs.com/algonote/p/7631564.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值