爆零之路的起点.....
考场上的代码有一个数组的下标敲错,然后爆零
自测几组数据竟然还过了......
emmmm
Code:
2018-10-01 19:51:01成功A掉
评价:普及+,提高-
思路:
首先很容易想到如何判断两条线有交点
就是在所给的字符串中找到两个一样的点
然后在这两个点之间搜索
记录两点之间所有点出现的次数,判断是否为偶数即可
然而这是片面的
我们需要找两个一样的点也是有限制的,我们要处理一下。
同时我们在记录交点的时候,不能单纯的凭借奇偶来判断,要采用特殊的方法
讲上面的那些废话只是因为基本思路比较重要.......
当然代码处理起来也特难受
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define size 100010
using namespace std;
string a;
int num[30],start[size],end[size],pos[size],posn[size],vis[size];
//num相当于一个桶,用于存26个字母出现的次数
//start存区间头,end存区间尾;
//pos和posn都存顺序,具体作用代码中解释
int sign[size],ans;
int main()
{
cin>>a;
int len=a.length();
for(int i=0;i<len;i++)
sign[i]=a[i]-96;
//把字母转换成数字来做
int tot=0;//用于统计区间的个数
//求出每一个区间的头和尾
for(int i=0;i<len;i++)
//枚举每一个字母
{
if(num[sign[i]]==0)
//如果这个数是0,即可以当做一个区间的开头
{
tot++;
start[tot]=i;//记录区间头
num[sign[i]]=1;//标记这里被当做开头
pos[sign[i]]=tot;// 记录下 第几组区间
posn[i]=tot;
}
else
//如果这个数不是0,即找到了区间的结尾
{
num[sign[i]]=0;//回溯,取消标记
end[pos[sign[i]]]=i;//对应的当前这一组的区间
posn[i]=pos[sign[i]];//记录
}
}
for(int i=1;i<=tot;i++)
//开始枚举每一个区间
{
int sum=0;
memset(vis,0,sizeof(vis));
for(int j=start[i]+1;j<end[i];j++)
//枚举区间中间的数
{
if(vis[posn[j]]==0)//如果没被访问过
{
vis[posn[j]]=1;//标记
sum++;//统计答案
}
else//如果之前访问过,即前面有字母与之对应
{
vis[posn[j]]=0; //取消标记
sum--;
}
}
ans+=sum;//累加答案
}
printf("%d",ans/2);//因为在计算的时候回多算,所以要 /2
return 0;
}
//By Yfengzi
纯净Code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define size 100010
using namespace std;
string a;
int num[30],start[size],end[size],pos[size],posn[size],vis[size];
int sign[size],ans;
int main()
{
cin>>a;
int len=a.length();
for(int i=0;i<len;i++)
sign[i]=a[i]-96;
int tot=0;
for(int i=0;i<len;i++)
{
if(num[sign[i]]==0)
{
tot++;
start[tot]=i;
num[sign[i]]=1;
pos[sign[i]]=tot;
posn[i]=tot;
}
else
{
num[sign[i]]=0;
end[pos[sign[i]]]=i;
posn[i]=pos[sign[i]];
}
}
for(int i=1;i<=tot;i++)
{
int sum=0;
memset(vis,0,sizeof(vis));
for(int j=start[i]+1;j<end[i];j++)
{
if(vis[posn[j]]==0)
{
vis[posn[j]]=1;
sum++;
}
else
{
vis[posn[j]]=0;
sum--;
}
}
ans+=sum;
}
printf("%d",ans/2);
fclose(stdin);
fclose(stdout);
return 0;
}
不在状态,再次爆零.....
跑最短路就可以了
跑的时候判断一下即可
良心的出题人并没有卡spfa
所以我们跑spfa
Code:
//2018-10-06 21:51:41正式切掉
//spfa就行了吧,照常求最短路,中间判断走不走特殊道路即可
//如果要走,那走几次最好,这在做spfa时都是可以记录的
//设f[i][j]表示走i此通道,到达j点的值
//如果通道的数量多余传送的次数,那我们就可以直接做spfa,中间处理一下他的 i 就行了,对于 j 我们直接搞spfa;
//如果通道的数量少于传送的次数,那就是裸的spfa了,因为如果传送次数多了,那就可能造成多余的浪费,还不如不走;
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define size 5005
using namespace std;
bool special[size];
int n,m,q,k,ans;
int tot,head[size],ver[size],next[size],edge[size];
void add(int x,int y,int z,bool love)
{
tot++;
ver[tot]=y;
edge[tot]=z;
special[tot]=love;
next[tot]=head[x];
head[x]=tot;
}
bool vis[600];
int f[2001][601];
queue<int> qwq;
//qwq
void spfa1()
{
//spfa
memset(f,0x7f,sizeof(f));
f[0][1]=0;
qwq.push(1);
vis[1]=1;
while(!qwq.empty())
{
int cur=qwq.front();
qwq.pop();
vis[cur]=0;
for(int i=head[cur];i;i=next[i])
{
int x=ver[i];
//判断走传送通道的次数
for(int j=0;j+special[i]<=k;j++)
{
if(f[j][cur]==0x7f7f7f7f) break;//如果cur这个点还没处理
if(f[j+special[i]][x]>f[j][cur]+edge[i])//如果走传送通道 到x这个点比不走传送通道大
{
f[j+special[i]][x]=f[j][cur]+edge[i];//转移
if(vis[x]==0)
{
vis[x]=1;
qwq.push(x);
}
}
}
}
}
}
//与spfa1基本一样,只是f的第一维全部变为0,因为我们不用传送通道
void spfa2()
{
memset(f,0x7f,sizeof(f));
f[0][1]=0;
qwq.push(1);
vis[1]=1;
while(!qwq.empty())
{
int cur=qwq.front();
qwq.pop();
vis[cur]=0;
for(int i=head[cur];i;i=next[i])
{
int x=ver[i];
if(f[0][x]>f[0][cur]+edge[i])
{
f[0][x]=f[0][cur]+edge[i];
if(vis[x]==0)
{
vis[x]=1;
qwq.push(x);
}
}
}
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&q,&k);
for(int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z,0);
}
for(int i=1;i<=q;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z,1);
}
if(q>=k)
{
spfa1();
int ans=0x7f7f7f7f;
for(int i=0;i<=k;i++)
ans=min(ans,f[i][n]);
printf("%d",ans==0x7f7f7f7f?-1:ans);
}
else
{
spfa2();
printf("%d",f[0][n]);
}
return 0;
}
//By Yfengzi
基础树形dp
//基础树形dp
\(f[i]\)代表使以\(i\)为根的树不连通的最小代价
易得转移方程
\(f[i]=min(edge[i],f[j])\)
即使得这棵树与叶子结点不连通的方法有两种
一种是删除与叶子节点相连的边,即\(edge[i]\)
一种是删除包括这些叶子结点的子树即 \(f[j]\)
Code:
//2018-10-08 10:17:30切掉
//基础树形dp
//f[i]代表使以i为根的树不连通的最小代价
//易得转移方程
//f[i]=min(edge[i],f[j])
//即使得这棵树与叶子结点不连通的方法有两种
//一种是删除与叶子节点相连的边,即edge[i]
//一种是删除包括这些叶子结点的子树即 f[j]
#include<iostream>
#include<cstring>
#include<cstdio>
#define size 100010
using namespace std;
int n,s,f[size];
bool vis[size];
int tot,ver[size*2],edge[size*2],head[size],next[size*2];
void add(int x,int y,int z)
{
tot++;
next[tot]=head[x];
edge[tot]=z;
ver[tot]=y;
head[x]=tot;
}
void dfs(int x)
{
vis[x]=1;
for(int i=head[x];i;i=next[i])
{
int y=ver[i];
if(vis[y]) continue;
dfs(y);
if(f[y]==0) f[x]+=edge[i];
//如果到达了边界,即到了遍历到了树的倒数第二层,这时只能删边
else f[x]+=min(f[y],edge[i]);
//dp
}
}
int main()
{
scanf("%d%d",&n,&s);
for(int i=1;i<=n-1;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs(s);
printf("%d",f[s]);
return 0;
}
//By Yfengzi
emmmm
数论...
直接把阶乘求出来是不可能的,所以我们要从\(n\)和\(k\)的关系入手
对于\(k\)进制下的数,要使其末尾为零,肯定要\(k\)的因数相乘。
那么我们考虑将\(k\)质因数分解
\(n!\)含几个\(k\)因数,然后将\(k\)包含的该质因子的个数除以\(n!\)中包含该质因子的个数,然后选出这个最小值即可
证明:
对于样例 \(n=10,k=10\)来说
\(k=2\times5=10\)
\(n!\)中包含8个2和2个5,则\(n!\)末尾有2个0,因为,其8个2除以\(k\)的一个2,得到8,然后其2个5除以\(k\)的一个5,得到2,即\(n!\)最多可以用两个2和两个5来组成k的倍数.
注意开long long!
Code:
//2018-10-08 11:14:10切掉
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const long long INF = ((1ll << 62) - 1 << 1) + 1;
long long n,k,s,tt;
long long tot1,tot2;
long long ans=INF;
int main()
{
scanf("%lld%lld",&n,&k);
s=sqrt(k);
for(int i=2;i<=s;i++)//将k质因数分解
{
if(k%i) continue;
tot1=0;tot2=0;
while(k%i==0)//统计质因子i在k总出现的次数
{
tot1++;
k/=i;
}
tt=n;
while(tt) //统计质因子i在n!中出现的次数
{
tot2+=tt/i;
tt/=i;
}
ans=min(ans,tot2/tot1);
}
if(k>1)//处理最后的一个质因数
{
tot2=0;
tt=n;
while(tt)
{
tot2+=tt/k;
tt/=k;
}
ans=min(ans,tot2);
}
printf("%lld",ans);
return 0;
}
//By Yfengzi
T5T6 已死亡