2019.11.11CSP-S模拟赛 世界线 时间机器 密码

43 篇文章 0 订阅
22 篇文章 0 订阅

A: 世界线

时限: 2 Sec  内存: 256 MB

题目描述

输入格式

输出格式

输入样例

5 5
1 2
1 3
2 3
3 4
4 5

输出样例

5

提示

 

 

题解

降智好题。。。。

考场上秒切bitset求传递闭包,马上开始写,写完算一下内存发现800+MB。。。。

然后就懵了。。。。

然后删掉了bitset的代码,写了个n^2暴力

 

其实直接把编号分一下组,多做几次dfs就好了。。。。

降智打击。。。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
using namespace std;
#define N 60005
#define M 100005
int fir[N],to[M],nxt[M],d[N],cnt;
void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
}
bitset<20005>s[N];
bool vis[N];
void dfs(int u)
{
	vis[u]=1;
	for(int v,p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(!vis[v]) dfs(v);
		s[u]|=s[v];
	}
}
int sum[N];
int main()
{
	//freopen("worldline.in","r",stdin);
	//freopen("worldline.out","w",stdout);
	int n,m,i,u,v;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++){
		scanf("%d%d",&u,&v);
		adde(u,v);d[u]++;
	}
	int l=1,r=20000;
	while(l<=n){
		memset(vis,0,sizeof(vis));
		for(i=1;i<=n;i++){
			s[i]&=s[0];
			if(i>=l&&i<=r) s[i].set(i-l+1);
		}
		for(i=1;i<=n;i++){
			if(!vis[i]) dfs(i);
			sum[i]+=s[i].count();
		}
		l+=20000;r+=20000;
	}
	long long ans=0;
	for(i=1;i<=n;i++)
		ans+=sum[i]-d[i]-1;
	printf("%lld",ans);
}

 

 

 

 

B: 时间机器

时限: 2 Sec  内存: 256 MB

题目描述

输入格式

输出格式

输入样例

3
2 2
1 4 2
3 5 1
1 4 2
2 5 1
3 2
1 3 1
2 4 1
3 5 1
1 3 2
2 5 1
2 2
1 2 2
1 2 1
1 2 1
1 2 2

输出样例

Yes
No
Yes

提示

 

 

 

 

题解

基础贪心(又被降智了。。。。)

满足一个节点的需求的必要条件是:一类电阻左端点小于该节点

当我们按左端点排序后,右端点就决定了当前电阻的适应力

显然,贪心地想一下,我们一个取适应力尽量小又满足条件的电阻,这样就可以为后面的选择留出更大的空间

然后set维护一下就好了

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 50005
struct node{
	int l,r,x,id;
}a[N],b[N];
bool cmp(node q,node w){return q.l<w.l||(q.l==w.l&&q.r<w.r);}
struct nod{
	int r,x,id;
	nod(){}
	nod(int w,int q,int e){r=w;x=q;id=e;}
	bool operator < (const nod &t)const{
		return r<t.r||(r==t.r&&id<t.id);
	}
}tmp;
set<nod> s;
set<nod>::iterator it1,it2,tit;
int main()
{
	//freopen("machine.in","r",stdin);
	//freopen("machine.out","w",stdout);
	int T,n,m,i,j;
	T=gi();
	while(T--){
		s.clear();
		n=gi();m=gi();
		for(i=1;i<=n;i++){a[i].l=gi();a[i].r=gi();a[i].x=gi();}
		for(i=1;i<=m;i++){b[i].l=gi();b[i].r=gi();b[i].x=gi();b[i].id=i;}
		sort(a+1,a+n+1,cmp);sort(b+1,b+m+1,cmp);
		bool flg=0;
		for(i=1,j=1;i<=n;i++){
			while(j<=m&&b[j].l<=a[i].l){
				s.insert(nod(b[j].r,b[j].x,b[j].id));
				j++;
			}
			it1=s.lower_bound(nod(a[i].r,0,0));
			while(a[i].x&&it1!=s.end()){
				if((*it1).x<=a[i].x){
					a[i].x-=(*it1).x;
					tit=it1;it1++;s.erase(tit);
				}
				else{
					tmp=(*it1);tit=it1;it1++;s.erase(tit);
					tmp.x-=a[i].x;s.insert(tmp);a[i].x=0;
				}
			}
			if(a[i].x&&it1==s.end()){printf("No\n");flg=1;break;}
		}
		if(!flg) printf("Yes\n");
	}
}

 

 

 

问题 C: 密码

时限: 1 Sec  内存: 256 MB

题目描述

输入格式

输出格式

输入样例

4 2 2

输出样例

2

提示

 

 

 

 

 

题解

题意就是求:

\sum_{i=0}^{N}\sum_{j=0}^{i}[p^k|C(i,j)]

数位DP。。。确实没想到(我太菜了2333)库默尔定理了解一下(其实也不太需要)

我们先展开一下组合数:

\sum_{i=0}^{N}\sum_{j=0}^{i}[p^k|\frac{i!}{j!(i-j)!}]

一个 n! 包含了多少个因子p呢?

cnt=\sum_{i=1}^{\infty}\left \lfloor \frac{n}{p^i} \right \rfloor

那么一个组合数C(n,m)呢?

cnt=\sum_{i=1}^{\infty}\left \lfloor \frac{n}{p^i} \right \rfloor -\left \lfloor \frac{m}{p^i} \right \rfloor -\left \lfloor \frac{n-m}{p^i} \right \rfloor

如果我们把n,m,n-m都表示成为p进制下的数的话

就会发现上面的式子表示:在截掉n,m,n-m的后i位,只把前面的剩下部分n',m',(n-m)'来计算n'-m'-(n-m)'

又因为m+(n-m)=n

所以C(n,m)中包含的因子p的个数就是m+(n-m)在p进制下的进位次数(如果要看严谨的证明可以去百度)

有了这个性质我们就可以数位DP了

先把N转为p进制数

设f[i][j][0/1][0/1]为前i位,已经进位了j次,下一位是否进位,和前i位是否与限制相等

然后发现前一位转移到当前位的贡献是一个等差数列的和,可以直接算

于是就没了。。。

std:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>

#define For(i, j, k) for(int i = j; i <= k; i++)
#define Forr(i, j, k) for(int i = j; i >= k; i--)

using namespace std;

const int N = 3510;
const int Mod = 1000000007;

typedef long long LL;

char S[N];
LL A[N], C[N], l;
int n, p, e;
int dp[N][N][2][2];

void divide(){
	LL s = 0;
	Forr(i, l, 1){
		s += A[i];
		A[i] = s / p, s %= p;
		s *= 10;
	}
	while(!A[l] && l > 0) --l;
	C[++n] = s / 10;
}

inline LL calc(int x){
	return (1LL * x * (x + 1) / 2) % Mod;
}

int main(){
	freopen("password.in", "r", stdin);
	freopen("password.out", "w", stdout);
	scanf("%s%d%d", S + 1, &p, &e);
	l = strlen(S + 1);
	Forr(i, l, 1) A[l + 1 - i] = S[i] - '0';
	while(l) divide();
	if(e > n){puts("0"); return 0;}
	reverse(C + 1, C + n + 1);
	dp[0][0][0][1] = 1;
	For(i, 0, n - 1) For(j, 0, e) 
		For(k, 0, 1){
			int cx = dp[i][j][k][0], cy = dp[i][j][k][1];
			For(v, 0, 1){
				int nj = j == e ? j : j + v;
				int &nx = dp[i + 1][nj][v][0], &ny = dp[i + 1][nj][v][1];
				if(!k){
					nx = (nx + 1LL * cx * calc(p - v)) % Mod;
					nx = (nx + 1LL * cy * calc(C[i + 1] - v)) % Mod;
					ny = (ny + 1LL * cy * (C[i + 1] - v + 1)) % Mod;
				}
				else{
					nx = (nx + 1LL * cx * calc(p + v - 1)) % Mod;
					nx = (nx + 1LL * cy * (calc(p + v - 1) - calc(p - C[i + 1] + v - 1) + Mod)) % Mod;
					ny = (ny + 1LL * cy * (p - C[i + 1] + v - 1)) % Mod;
					 is this!!! 
				}     
			
			}
		}
	printf("%d\n", (dp[n][e][0][0] + dp[n][e][0][1]) % Mod);
	return 0;
}

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值