比赛链接:https://www.luogu.org/contest/show?tid=1176
这套题的题目质量很高,写一发题解。
T1:
Cjwssb最近在物理学科上遇到了难题,他不会计算一个电路中的总电阻,现在他找到了你,希望你能帮助他。
这个电路有如下限定
1、 电路只由导线以及电阻为一欧的电阻组成
2、 保证电路从左到右连接,即每个电阻或导线的两个连接点xy保证x
3、 保证接线柱1为电源正极,接线柱n为电源负极
4、 保证每个接线柱只会被串联或者并联两个分支电路或者不接任何电线或电阻.
我们可以用dfs求解,这个电路中就是普通的混连电路,我们可以考虑,对于当前节点,如果它有两个分支,我们就分别算出这两个分支的电阻然后用公式计算并联后的总电阻,如果只有一个分支,我们就让它到下一个分支即可,用一个返回类型的double的函数,dfs(s,t),返回的是s,t这两个节点中间的电阻值是多少,然后具体的细节看代码就行了。
#include
#include
#include
using namespace std;
#define maxn 200000
int n,m,rd[maxn],cd[maxn],l;
int pre[maxn],last[maxn],other[maxn],len[maxn];
int a[maxn],s[maxn],pp[maxn],top;
double ans;
void connect(int x,int y,int z)
{
l++;
pre[l]=last[x];
last[x]=l;
other[l]=y;
len[l]=z;
}
double dfs(int s,int t)
{
double temp=0;
if (s==t) return temp;
if (cd[s]==2)
{
int p=last[s];
int v1=other[p];
double R1=dfs(v1,pp[s])+len[p];
p=pre[p];
int v2=other[p];
double R2=dfs(v2,pp[s])+len[p];
if (R1==0||R2==0) temp=0;
else temp=(R1*R2)/(R1+R2);
temp+=dfs(pp[s],t);
}
else
{
int p=last[s];
int v=other[p];
temp=dfs(v,t)+len[p];
}
return temp;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
rd[b]++;cd[a]++;
connect(a,b,c);
}
for (int i=1;i<=n;i++)
{
if (rd[i]==2)
{
pp[s[top]]=i;
top--;
}
if (cd[i]==2) s[++top]=i;
}
ans=dfs(1,n);
printf("%.3lf\n",ans);
return 0;
}
Cjwssb得到了你的帮助,显得很开心,跟你道谢,你回谢了他。但随后他由于少听了一些词,对你的回谢造成了误会,这使你们两个产生了隔阂,你很不甘心,于是打算告诉他这句话有几种理解方式,以告诉他他误解你了。一句话的理解方式如下
对于一句原句s1和听到的句子s2,理解方式是用听到的句子替换掉原句的相同部分,替换成*,使得原句形成一个新的句子,以达到新的意思,你的任务是统计有多少种意思.
首先我们可以用KMP或者字符串hash预处理出s2在s1中出现的位置,然后由于将一个s2换成*后,s2的这个位置的字母就全不能用了,因此我们用DP统计答案,令dp[i]表示到s1考虑到i这个位置的理解方式有几种,那么dp[i]=dp[i-1](s1[i]不是匹配位置),dp[i]=dp[i-1]+dp[i-len2](s1[i]是匹配位置)。
#include
#include
#include
using namespace std;
#define maxn 100005
const long long mod=1000000007;
typedef unsigned long long ull;
const ull base=163;
char s1[maxn],s2[maxn];
ull hash[maxn],goal,x[maxn];
int T,len1,len2,tot,cnt;
long long dp[maxn];
bool flag[maxn];
ull getH(int l,int r)
{
return hash[r]-hash[l-1]*x[r-l+1];
}
int main()
{
x[0]=1;
for (int i=1;i<=maxn-5;i++) x[i]=x[i-1]*base;
scanf("%d",&T);
while (T--)
{
scanf("%s",s1+1);len1=strlen(s1+1);
scanf("%s",s2+1);len2=strlen(s2+1);
goal=0;tot=0;
memset(flag,0,sizeof flag);
for (int i=1;i<=len1;i++) hash[i]=hash[i-1]*base+s1[i]-'a'+1;
for (int i=1;i<=len2;i++) goal=goal*base+s2[i]-'a'+1;
for (int i=len2;i<=len1;i++)
if (getH(i-len2+1,i)==goal) flag[i]=1;
dp[0]=1;
for (int i=1;i<=len1;i++)
{
dp[i]=dp[i-1];
if (flag[i])
dp[i]=(dp[i]+dp[i-len2])%mod;
}
printf("Case #%d: %lld\n",++cnt,dp[len1]);
}
return 0;
}
T3:
cjwssb知道是误会之后,跟你道了歉。你为了逗笑他,准备和他一起开始膜蛤。不过你的时间不多了,但是更惨的是你还需要完成n个膜蛤任务。假设你当前的时间为T,每个任务需要有一定的限制ti表示只有当你的T严格大于ti时你才能完成这个任务,完成任务并不需要消耗时间。当你完成第i个任务时,你的时间T会加上bi,此时要保证T在任何时刻都大于0,那么请问你是否能完成这n个膜蛤任务,如果可以,输出+1s,如果不行,输出-1s。
我们可以贪心,首先可以知道一定是先按t升序做b>=0的任务,然后对于b<0的任务,我们可以将其按t+b从大到小排序,我们可以用基于冒泡排序的贪心证明方法证明,具体的证明就是看如果只交换两个任务的位置,这两个任务的t和b满足怎样的关系会使其更优,然后我们按照这种关系排序即可。
#include
#include
#include
using namespace std;
#define maxn 100005
int z,n,tot,cnt;
long long T;
struct Vergil
{
int t,b;
}t[maxn];
bool cmp(Vergil a,Vergil b)
{
return a.t(b.t+b.b);
}
int main()
{
scanf("%d",&z);
while (z--)
{
scanf("%d%lld",&n,&T);
cnt=n;tot=0;bool ok=0;
for (int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if (y>=0)
{
t[++tot].t=x;
t[tot].b=y;
}
else
{
t[cnt].t=x;
t[cnt].b=y;
cnt--;
}
}
sort(t+1,t+tot+1,cmp);
for (int i=1;i<=tot;i++)
if (T>t[i].t&&T+t[i].b>0) T+=(long long)t[i].b;
else
{
printf("-1s\n");
ok=1;
break;
}
if (ok) continue;
sort(t+tot+2,t+n+1,cmp1);
for (int i=tot+1;i<=n;i++)
if (T>t[i].t&&T+t[i].b>0) T+=(long long)t[i].b;
else
{
printf("-1s\n");
ok=1;
break;
}
if (!ok) printf("+1s\n");
}
return 0;
}