20191007GKurumi爆0记 2
by GKurumi
T1、那一天我们许下约定
题目描述:(保密)
思路分析,想了10多分钟,dp打了10多分钟,之后发现d原来那么大。
只能拿30分
30分代码(程序有点锅,不要贸然复制粘贴):
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int n,m,d,f[2001][2001];
int main()
{
cin>>n>>d>>m;
while(n&&m&&d)
{
memset(f,0,sizeof(f));
f[0][0]=1;
for(int i=1;i<=d;i++)
{
f[0][i]=1;
}
for(int i=1;i<=n;i++)
{
for(int k=1;k<=d;k++)
{
for(int j=min(m-1,i);j>=0;j--)
{
if(i>=j)f[i][k]=(f[i][k]+f[i-j][k-1])%mod;
}
}
}
cout<<f[n][d]<<endl;
cin>>n>>d>>m;
}
return 0;
}
之后我们又想如何消掉d(考场先做的T2),我们定义f[ i ] [ j ],第一维存几天,第二维存给多少饼干的方案数。我们可得到一个式子。\(f[i][j] = \sum^{j - 1}_{k = j - m - 1}f[j - 1][k]\)
再往下想,直接遍历还是会超时,我们又需要一个前缀和算出每天给多少饼干的方案数。
处理完,算一遍方案数,不难想到,n如果小于d时,即使每天给一个的话也会有几天给不到,所以需先判断n于d的大小关系。统计方案就是吧多少天给多少饼干的方案加起来就ok。
\[ans = \sum^n_{i = 1}{f[i][j]}\times C\tbinom{i}{d}\]
这样思路就清晰了
之后放上代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='0')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}
return x*f;
}
const int mod=998244353;
int n,m,d,f[2010][2010];
int sum[4010],ans;
int ksm(int a,int b)
{
int ans=1;
while(b)
{
if(b&1)
{
ans=a*ans%mod;
}
a=a*a%mod;
b>>=1;
}
return ans%mod;
}
signed main()
{
// freopen("contract.in","r",stdin);
// freopen("contract.out","w",stdout);
n=read();d=read();m=read();
while(n!=0||m!=0||d!=0)
{
memset(f[1],0,sizeof(f[1]));
f[0][0]=1;
ans=0;
for(int i=1;i<=min(n,m-1);i++)
{
f[1][i]=1;
}
for(int i=2;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
sum[j]=((sum[j-1])%mod+(f[i-1][j])%mod)%mod;
}
for(int j=1;j<=n;j++)
{
f[i][j]=(sum[j-1]%mod-sum[max(j-m,0ll)]%mod+mod)%mod;
}
}
int now=d%mod;
for(int i=1;i<=min(n,d);i++)
{
if(i==1)
{
ans=((ans+now*f[i][n]%mod)%mod);
ans%=mod;
}
else
{
now=(now%mod*ksm(i,mod-2)%mod*((d-i+1)%mod))%mod;
ans%=mod;
ans=(ans+f[i][n]%mod*now%mod)%mod;
}
}
cout<<(ans+mod)%mod<<endl;
n=read();d=read();m=read();
}
return 0;
}
T2、那一天她离我而去
题目描述:(加密)
思路清晰,找最小环:
不说了直接上代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int t,n,m,ans=0x3f3f3f3f;
int head[40010],cnt=1,to[40010],val[40010],nxt[40010];
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='0')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch-'0');ch=getchar();}
return x*f;
}
void add(int u,int v,int w)
{
val[++cnt]=w;
to[cnt]=v;
nxt[cnt]=head[u];
head[u]=cnt;
}
int dis[40010];
typedef pair<int,int> p;
priority_queue<p,vector<p>,greater<p> > q;
bool vis[40010];
void dij(int x)
{
memset(dis,0,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[x]=0;
q.push(p(0,x));
while(!q.empty())
{
int u=q.top().second;
q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(dis[v]>dis[u]+val[i])
{
dis[v]=dis[u]+val[i];
q.push(p(dis[v],v));
}
}
}
}
signed main()
{
freopen("leave.in","r",stdin);
freopen("leave.out","w",stdout);
t=read();
while(t--)
{
n=read();m=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read(),w=read();
add(u,v,w);add(v,u,w);
}
for(int i=head[1];i;i=nxt[i])
{
int tmp=val[i];
val[i]=val[i^1]=0x3f3f3f3f;
dij(1);
ans=min(ans,dis[to[i]]+tmp);
val[i]=val[i^1]=tmp;
}
if(ans==0x3f3f3f3f)ans=-1;
//cout<<ans<<endl;
printf("%lld\n",ans);
ans=0x3f3f3f3f;
cnt=1;
memset(head,0,sizeof(head));
}
return 0;
}
/*
2
3 3
1 2 1
2 3 1
3 1 1
4 5
1 2 2
2 3 2
3 4 2
1 4 2
1 3 5
*/
n^2logn可能会T。
T3、哪一天她能重回我身边
题目描述:(加密)
神仙题,我这个蒟蒻是不会了,看题解是换根dp
大概思路就是:
第一次dp出以任意点u的答案 (只需要计算让所有边都指向父亲需要反转多少次)。
第二次dp出以每个点为根的答案。
取最小值,统计最小值的方案次数。
特别的: 基环树的方案只可能有1或两种,只需要判断顺逆时针的反转次数是否相同即可(具体来说只需要判断以环上相邻两点分别为根的答案是否相等即可)。
最后将每个联通块的最小值相加,方案数相乘。
上代码吧
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define ll long long
using namespace std;
const int MAXN=100100,D=998244353;
int T;
int n;
struct node
{
int to,nxt;
}mp[MAXN*2];
int h[MAXN*2],tot;
void add(int x,int y)
{
mp[++tot].nxt=h[x];
mp[tot].to=y;
h[x]=tot;
}
bool flag;
int vcnt,ecnt;
bool vis[MAXN*2];
void dfs(int u)
{
vcnt++;vis[u]=1;
for(int i=h[u];i;i=mp[i].nxt)
{
int v=mp[i].to;
++ecnt;
if(vis[v]) continue;
dfs(v);
}
}
int s,nxt,id;
ll num,f[MAXN*2],g[MAXN*2],ans,anscnt;
void dfs1(int u,int fa)
{
vis[u]=1;
for(int i=h[u];i;i=mp[i].nxt)
{
int v=mp[i].to;
if(v==fa) continue;
if(vis[v])
s=u,nxt=v,id=i;
else
{
dfs1(v,u);
f[u]+=f[v]+(i&1);
}
}
}
vector<int> vv;
void dfs2(int u,int fa)
{
vv.push_back(g[u]);
for(int i=h[u];i;i=mp[i].nxt)
{
int v=mp[i].to;
if(v==fa) continue;
if(i==id||i==(id^1)) continue;
g[v]=g[u]+((i&1)?-1:1);
dfs2(v,u);
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
tot=1;flag=0;
memset(h,0,sizeof(h));
memset(vis,0,sizeof(vis));
scanf("%d",&n);
for(int i=1,aa,bb;i<=n;i++)
{
scanf("%d%d",&aa,&bb);
add(bb,aa);add(aa,bb);
}
for(int i=1;i<=2*n;i++)
if(!vis[i])
{
vcnt=ecnt=0;
dfs(i);
if(ecnt/2>vcnt)
{
printf("-1 -1\n");
flag=1;
break;
}
}
if(flag) continue;
memset(vis,0,sizeof(vis));
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
ans=0;anscnt=1;
for(int i=1;i<=2*n;i++)
if(!vis[i])
{
vv.clear();
s=nxt=id=0;
num=0;
dfs1(i,0);
g[i]=f[i];
dfs2(i,0);
if(!id)
{
sort(vv.begin(),vv.end());
for(int j=0;j<vv.size();j++)
if(vv[j]!=vv[0]) break;
else ++num;
ans+=vv[0];
}
else
{
id&=1;
if(g[s]+(id^1)==g[nxt]+id) num=2;
else num=1;
ans+=min(g[s]+(id^1),g[nxt]+id);
}
anscnt=anscnt*num%D;
}
printf("%lld %lld\n",ans,anscnt);
}
return 0;
}
T3参考G_keng的博客,附上原链接
谢谢。