前言
今天虽然题目难度不大,但是我打爆了。
虽然一开始都想到正解,但后来都没打出来,不敢浪,就先打了个暴力,就第一题直接打的正解。
但是翻车了。。。第一题只有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] = (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
*/