比赛时间:2018.10.11 选手:lrllrl 得分:100+100+0=200 用时:2小时
A.黑魔法师之门
最初以为是求欧拉回路的数量,后来发现不可做,再后来发现了规律。
#include<cstdio>
#include<cstring>
const int N=2e5+10,mod=1e9+9;
int n,m,ans=1,f[N];
inline int findf(int x){return f[x]==x?x:f[x]=findf(f[x]);}
int main()
{
freopen("magician.in","r",stdin);
freopen("magician.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)f[i]=i;
while(m--)
{
int a,b;
scanf("%d%d",&a,&b);
int fa=findf(a),fb=findf(b);
if(fa==fb)ans=ans*2%mod;
else f[fa]=fb;
printf("%d\n",(ans-1+mod)%mod);
}
return 0;
}
B.守卫者的挑战
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define _rep(i,a,b) for(int i=(a);i<=(b);i++)
#define _for(i,a,b) for(int i=(a);i<(b);i++)
int a[201],N,L,K;
double ans,p[201],f[2][201][401];
int main()
{
freopen("guard.in","r",stdin);
freopen("guard.out","w",stdout);
scanf("%d%d%d",&N,&L,&K),K=min(K,N);
_rep(i,1,N)scanf("%lf",&p[i]),p[i]/=100.000000;
_rep(i,1,N)scanf("%d",&a[i]);
f[0][0][K+200]=1.000000;
_for(i,0,N)
{
memset(f[(i+1)&1],0,sizeof(f[(i+1)&1]));
_rep(j,0,N)_rep(k,-N+200,N+200)
if(a[i+1]==-1)
f[(i+1)&1][j+1][k-1]+=f[i&1][j][k]*p[i+1],f[(i+1)&1][j][k]+=f[i&1][j][k]*(1.000000-p[i+1]);
else
f[(i+1)&1][j+1][min(N+200,k+a[i+1])]+=f[i&1][j][k]*p[i+1],f[(i+1)&1][j][k]+=f[i&1][j][k]*(1.000000-p[i+1]);
}
_rep(j,L,N)_rep(k,0,N)
ans+=f[N&1][j][k+200];
printf("%.6f\n",ans);
return 0;
}
C.终极武器
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
#define rg register
#define x first
#define y second
#define MP(a,b) make_pair(a,b)
const int N=1e4+10;
struct node{
int dat,side;//dat排序前的下标,side=1为左,side=2为右
ll low;
node(){}
node(ll _a,int _b,int _c):low(_a),dat(_b),side(_c){}
bool operator <(const node&rhs)const{
return low<rhs.low;}
}c[N*2];
pair<ll,ll> a[N],b[N];
int n,k,m,link,tot,i,j,f[N*3][10];
bool g[10][10],v[10];//9*9的邻接矩阵
map<ll,int>hx;//离散化
inline void change(node a){a.side==1?b[a.dat].x--:b[a.dat].y--;}
//可以发现最初第 k 位上的数字的其中一组是[L/10^k %10 +1, R/10^k %10 +1),当后面的位经
//过 L%10^k 这个数的时候区间左端减一,经过 R%10^k 这个数的时候区间右端减一。
inline void Crash(int v[10])//删去不能互相转化的数之间的边
{
for(int i=1;i<=9;i++)
for(int j=1;j<=9;j++)
if(v[i]&&!v[j]&&g[i][j])g[i][j]=g[j][i]=0,link-=2;
}
inline void calc()//特殊处理个位情况
{
int i,j,l,r;ll now,L,R;
for(now=-1,i=1;i<=n;i++,now=R)
{
l=a[i].x%10,r=a[i].y%10;//个位上是开区间
L=a[i].x/10,R=a[i].y/10;
if(L!=now&&now>0)Crash(f[0]),memset(f[0],0,sizeof(f[0]));//已经不再一个区间里了
if(L!=R)//除去个位是不一样的,所以只需分别满足
{
for(j=l;j<=9;j++)f[0][j]++;//从l到9互相转化不会比左区间更小
Crash(f[0]);memset(f[0],0,sizeof(f[0]));
for(j=1;j<r;j++)f[0][j]++;//从1到r互相转化不会比右区间更大
}
else for(j=l;j<r;j++)f[0][j]++;//l到r互相转化同时满足不会变小变大
}
Crash(f[0]);
}
inline bool solve(int k)//当前枚举到第k位
{
ll l,r,L,R,pow=1,now;
int i,j,p,q,num=1;
memset(f,0,sizeof(f));
for(m=tot=i=0;i<k;i++)pow*=10;//需要乘的幂
for(i=1;i<=n;i++)
{
L=a[i].x/pow,R=a[i].y/pow;
if(!L&&!R)continue;
l=a[i].x%pow,r=a[i].y%pow;
b[++m]=MP(L+1,R+1);
c[++tot]=node(l,m,1);c[++tot]=node(r,m,2);
}
if(!m)return 0;//所有区间都处理完了
hx.clear();sort(c+1,c+tot+1);//排序离散化
for(p=1;p<=tot&&!c[p].low;p++)change(c[p]);//更新两端
for(i=1,now=-1;i<=m;i++,now=R)
{
l=b[i].x%10,r=b[i].y%10;
L=b[i].x/10,R=b[i].y/10;
if(L!=now&&now>0)hx[now]=num++;
if(L!=R)
{
for(j=l;j<=9;j++)f[num][j]++;
hx[L]=num++;
for(j=1;j<r;j++)f[num][j]++;
}
else for(j=l;j<r;j++)f[num][j]++;
}
hx[now]=num++;
for(i=1;i<num;i++)Crash(f[i]);
for(;p<=tot;p=q)
{
for(q=p;q<=tot&&c[q].low==c[p].low;q++)
{
change(c[q]);
now=c[q].side==1?b[c[q].dat].x:b[c[q].dat].y;
k=now%10,now/=10;
if(!hx[now])hx[now]=num++;
now=hx[now];
if(c[q].side==1)f[now][k]++;else f[now][k]--;
}
for(i=1;i<num;i++)Crash(f[i]);
}
return 1;
}
int main()
{
//freopen("in.txt","r",stdin);
cin>>n>>k;
for(i=1;i<=n;i++)
cin>>a[i].x>>a[i].y,a[i].y++;
memset(g,1,sizeof(g));link=72;//9*9的完全图
calc();
for(i=1;i<k&&solve(i)&&link;i++);//批量处理剩下k-1位
for(i=1;i<=9;i++)
if(!v[i])
{
cout<<i,v[i]=1;
for(j=1;j<=9;j++)
if(!v[j]&&g[i][j])cout<<j,v[j]=1;
cout<<endl;
}
return 0;
}
比赛总结
这次比赛题比上次要难不少。T1将近一个小时才做出来,T2的滚动数组也写炸,T3就不谈了……
存在的问题:
T1的规律没有找出来,T2状态定义有问题且滚动数组打错,T3完全打不动(毒瘤题不想看)
继续补DP专题吧