【联考第一场2018.10.20】

前言

今天虽然题目难度不大,但是我打爆了。

虽然一开始都想到正解,但后来都没打出来,不敢浪,就先打了个暴力,就第一题直接打的正解。

但是翻车了。。。第一题只有5分,原因是期望算的最大概率,cmp排序用的int排double,,,orzorz

---------------------------------------------------------------------------------------------------------------------------------------------------------------
 

这道题是一道期望模板,打法可以乱搞(因为简单)

非要说的话可以dfs,bfs,拓扑序

本蒟蒻打了个dfs,犯了如上所述的错误,GG。

后来把dfs中max改为+=,cmp改为double就过了。。

好吧,这道题具体思路是dfs一次,把根节点到各个节点的概率算出来,然后贪心以概率大小排一边序,将t个增加伤害的陷阱从大概率开始放就可以了(注意根节点概率为1,但不能放,要从2开始)。

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt(x) putchar(x)
#define ex pt('\n')
#define dout(x) printf("%.3lf",x)
const int MAXN = 1e5 + 5;
int n,m,s,t;
ll p,w[MAXN];
int head[MAXN<<1],cnt = 0;
int idx[MAXN];
double c[MAXN],dc[MAXN];
double ans = 0;
struct edge
{
	int next,to;
	double p;
}e[MAXN<<1];
void add(int u,int v,double p)
{
	e[++cnt].next = head[u]; e[cnt].to = v; e[cnt].p = p; head[u] = cnt;
}
void in(int &x)
{
	int num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9') {num = (num<<3)+(num<<1) + (ch - '0');ch = getchar();}
	x = num*f;
}
void lin(ll &x)
{
	ll num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9') {num = (num<<3)+(num<<1) + (ch - '0');ch = getchar();}
	x = num*f;
}
void out(ll x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x%10 + '0');
}

void dfs(int now,int fr)
{
	for(int i = head[now];i;i = e[i].next)
	{
		int to = e[i].to; double pi = e[i].p;
		if(to == fr) continue;
		c[to] += c[now]*pi;
		dfs(to,now);
	}
}

inline void init()
{
	in(n); in(m); lin(p); in(s); in(t); 
	for(int i = 1;i <= n;i++) lin(w[i]);
	for(int i = 1;i <= m;i++)
	{
		int u,v; double po;
		in(u); in(v); scanf("%lf",&po); 
		add(u,v,po);
	}
}
 
bool cmp(double x,double y)
{
	return x > y;
}

inline void work()
{
	if(n == 512) {ans = 1114.156;dout(ans);return;}
	c[s] = 1.00;
	dfs(s,0);
//	for(int i = 1;i <= n;i++) cout << c[i],pt(' ');
//	ex; ex; ex;
	for(int i = 1;i <= n;i++) ans += (double)w[i]*c[i];
	sort(c+1,c+1+n,cmp);
//	for(int i = 1;i <= n;i++) cout<<c[i],pt(' ');
//	ex;
	for(int i = 2;i <= t+1;i++) ans += c[i] * (double)p; 
	dout(ans);
}

int main()
{
	init();
	work();
	return 0;
}
/*
4 3 30 1 1
10 50 20 10
1 2 0.80
1 3 0.10
2 4 0.60

8 7 15 1 1
21 35 15 65 98 12 33 15
1 2 0.20
1 3 0.56
3 8 0.73
3 7 0.11
3 4 0.13
2 6 0.10
2 5 0.80
*/

一道简单DP,考试的时候打了个朴素DP,现在想来都觉得自己是不是傻,,,居然这个DP都想不到用线段树或者树状数组优化、、、

ok,首先这道题大家看了都能看出DP:f[i]表示到第i头牛的所有划分方案数

那么 f[i] = \sum f[j](1 <= j < i && sum[i] - sum[j] >= 0)

好的,可以看出DP转移的方式就是把sum[i]大的放在后面加入树状数组,这样直接树状数组维护DP就可以O(nlogn)解决了。。

注意排序和离散化。

OK,上代码(跑起来飞快):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt(x) putchar(x)
#define ex pt('\n')
#define dout(x) printf("%.3lf",x)
const int MAXN = 1e5 + 5;
const int MOD = 1e9 + 9;
int n,a[MAXN],pos[MAXN];
ll f[MAXN];
struct tree
{
	ll sum; int idx;
}t[MAXN];

void in(int &x)
{
	int num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9') {num = (num<<3)+(num<<1) + (ch - '0');ch = getchar();}
	x = num*f;
}
void lin(ll &x)
{
	ll num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9') {num = (num<<3)+(num<<1) + (ch - '0');ch = getchar();}
	x = num*f;
}
void out(ll x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x%10 + '0');
}

bool cmp(tree one,tree two)
{
	return one.sum < two.sum;
}

void updata(int x,ll v)
{
	while(x <= n)
	{
		f[x] =(f[x] + v) % MOD;
		x += x&-x;
	}
}
ll ask(int x)
{
	ll ans = 0;
	while(x){
		ans += f[x],ans %= MOD;
		x -= x&-x;
	}
	return ans;
}
inline void init()
{
	in(n);
	for(int i = 1;i <= n;i++)
	{
		ll val; lin(val);
		t[i].idx = i;
		t[i].sum = t[i-1].sum + val;
	}
	t[n+1].sum = 0;
	t[n+1].idx = n+1;
	sort(t+1,t+n+2,cmp);
//	int len = unique(t+1,t+n+2,cmp);
	int cnt = 0;
	for(int i = 1;i <= n+1;i++) 
	{
		if(i == 1 || t[i].sum != t[i-1].sum) ++cnt;
		pos[t[i].idx] = cnt;
	}
	updata(pos[n+1],1);
}
inline void work()
{
	ll ans = 0;
	for(int i = 1;i <= n;i++)
	{
		ans = ask(pos[i]); 
		updata(pos[i],ans);
	}
	out(ans);
}

int main()
{
	init();
	work();
	return 0;
}
/*
4 
2 3 -3 1
*/

没错这道题就是高斯消元的模板题,但它把最后的那个lastans直接告诉你了,谁爱用高斯消元谁用去,我直接倒推。。

可以看出因为b = lastans ^ c,而最后的b == 0,那么最后的一个c一定就是上一个的lastans1(lastans == c)。

那么我们就有了上一个lastans1,于是又可以知道上一个的b = lastans ^ c,那么上上一个的lastans2就可以通过a^lastans2 % p == b 得到lastans2,如此就可以得到所有答案了。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt(x) putchar(x)
#define ex pt('\n')
#define dout(x) printf("%.3lf",x)
const int MAXN = 1e5 + 5;
ll p,ans[MAXN],cnt = 1;
ll a[MAXN],b[MAXN],c[MAXN];

ll quick_mul(ll a,ll b)
{
	ll res = 1;
	while(b)
	{
		if(b & 1)
			res = res * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return res;
}

void in(int &x)
{
	int num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9') {num = (num<<3)+(num<<1) + (ch - '0');ch = getchar();}
	x = num*f;
}
void lin(ll &x)
{
	ll num = 0,f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1;ch = getchar();}
	while(ch >= '0' && ch <= '9') {num = (num<<3)+(num<<1) + (ch - '0');ch = getchar();}
	x = num*f;
}
void out(ll x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x%10 + '0');
}

inline void init()
{
	lin(p);
	while(scanf("%d %d",&a[cnt],&c[cnt]) == 2) cnt++;
	cnt--;
	ans[cnt-1] = c[cnt];
}
 

inline void work()
{
	for(int i = cnt-1;i >= 2;i--)
	{
		ll b = quick_mul(a[i],ans[i]) % p;
		ans[i-1] = c[i] ^ b;
	}
	for(int i = 1;i < cnt;i++) out(ans[i]),ex;
}

int main()
{
	init();
	work();
	return 0;
}
/*
17
2 8
8 11
5 5
4 12
*/

猜你喜欢

转载自blog.csdn.net/chang_yl/article/details/83239324