7.9集训模拟赛12(又是虎哥出题的一天)
A. 狡猾的商人
题目描述
***姹接到一个任务,为税务部门调查一位商人的账本,看看账本是不是伪造的。账本上记录了n个月以来的收入情况,其中第i 个月的收入额为Ai(i=1,2,3...n-1,n)。当 Ai大于0时表示这个月盈利Ai 元,当 Ai小于0时表示这个月亏损Ai 元。所谓一段时间内的总收入,就是这段时间内每个月的收入额的总和。
***姹的任务是秘密进行的,为了调查商人的账本,她只好跑到商人那里打工。她趁商人不在时去偷看账本,可是她无法将账本偷出来,每次偷看账本时她都只能看某段时间内账本上记录的收入情况,并且她只能记住这段时间内的总收入。 现在,***姹总共偷看了m次账本,当然也就记住了m段时间内的总收入,你的任务是根据记住的这些信息来判断账本是不是假的。
输入格式
第一行为一个正整数w,其中w < 100,表示有w组数据,即w个账本,需要你判断。
每组数据的第一行为两个正整数n和m,其中n < 100,m < 1000,分别表示对应的账本记录了多少个月的收入情况以及偷看了多少次账本。
接下来的m行表示***姹偷看m次账本后记住的m条信息,每条信息占一行,有三个整数s,t和v,表示从第s个月到第t个月(包含第t个月)的总收入为v,这里假设s总是小于等于t。
输出格式
包含w行,每行是true或false。
其中第i行为true当且仅当第i组数据,即第i个账本不是假的;第i行为false当且仅当第i组数据,即第i个账本是假的。
样例
样例输入
2 3 3 1 2 10 1 3 -5 3 3 -15 5 3 1 5 100 3 5 50 1 2 51
样例输出
true false
分析
这一道题就是带权并查集,给出了[l , r]的区间和,相当于s[r] - s[l],一旦已经知道了s[a]-a[b],s[b]-s[c],显然在出一条[a,c]就可以判断“账单的真假了”,将每条这样的信息放入一个集合中,用并查集来维护,并维护cha[l] = s[root]-s[l],char[r] = s[root] - s[r],若l,r已经在一个集合中就直接查询cha[l]-cha[r]是否与w相等即可。
#include<bits/stdc++.h> int fa[105],cha[105]; int find(int x) { if(x!=fa[x]) { int t=find(fa[x]); cha[x]+=cha[fa[x]]; fa[x]=t; } return fa[x]; } int main() { int T,n,m,i,x,y,z,flag; scanf("%d",&T); while (T--) { flag=0; scanf("%d%d",&n,&m); for(i=0;i<=n;i++) { fa[i]=i; cha[i]=0; } for(i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&z); x--; if(find(x)!=find(y)) { cha[fa[y]]=cha[x]-cha[y]-z; fa[fa[y]]=fa[x]; } else if(cha[x]-cha[y]!=z) flag=1; } if(flag==0) printf("true\n"); else printf("false\n"); } return 0; }
B. 小象和老鼠
题目描述
S国的动物园是一个N*M的网格图,左上角的坐标是(1,1),右下角的坐标是(N,M)。小象在动物园的左上角,它想回到右下角的家里去睡觉,但是动物园中有一些老鼠,而小象又很害怕老鼠。动物园里的老鼠是彼此互不相同的。小象的害怕值定义为他回家的路径上可以看见的不同的老鼠的数量。若小象当前的位置为(x1,y1),小象可以看见老鼠,当且仅当老鼠的位置(x2,y2)满足|x1-x2|+|y1-y2|<=1。由于小象很困了,所以小象只会走一条最近的路回家,即小象只会向下或者向右走。现在你需要帮小象确定一条回家的路线,使得小象的害怕值最小。
输入格式
第一行包含两个用空格隔开的整数,N和M。
接下来一个N*M的矩阵表示动物园的地图。其中A[i][j]表示第i行第j列上老鼠的数量。 若A[i][j]=0则表示当前位置上没有老鼠(小象的家里也可能存在老鼠)。
输出格式
输出一个整数,表示路线最小的害怕值是多少。
样例
样例输入
3 9 0 0 1 0 0 0 0 0 1 1 1 1 1 1 1 0 1 0 1 0 0 1 0 0 1 0 0
样例输出
9
数据范围与提示
对于10%的数据,1<=N,M<=5。
对于100%的数据,1<=N,M<=1000,0<=Aij<=100。
分析
这是一道dp题,显然吧,我们可以定义到达第i行第j列看到的最少的老鼠,但是小象看到的不能是同一个老鼠也就是一个位置的老鼠只算一次,由于小象只能向下或者向右走,所以我们再加以为来表示小象是从上还是从左边转移过来。
f[i][j][0]表示到达i行j列并且是从上方转移过来的最少看到的老鼠。
f[i][j][1]表示到达i行j列并且是从左边转移过来的看到最少的老鼠。
f[i][j][0]:
f[i-1][j]的状态不知道,所以还有分类,若f[i][j][0],那么a[i][j]的左边下边右边的老鼠(如果有)要计入其中。
若f[i][j][1],那么a[i][j]的下边右边的老鼠要计入其中。
f[i][j][1]:
若f[i][j-1][0],a[i][j]的下边右边要计入。
若f[i][j-1][1],a[i][j]的左边下边右边要计入其中。
然后初始化就好想了吧。
#include<bits/stdc++.h> using namespace std; const int N = 1010; int n,m; int a[N][N]; int f[N][N][2]; int main(){ scanf("%d%d",&n,&m); for(int i = 1;i <= n;i++){ for(int j = 1;j <= m;j++){ scanf("%d",&a[i][j]); } } memset(f,0x3f,sizeof(f)); f[1][1][1] = f[1][1][0] = a[1][1] + a[1][2] + a[2][1]; for(int i = 1;i <= n;i++){ for(int j = 1;j <= m;j++){ f[i][j][0] = min(f[i][j][0],f[i-1][j][0]+a[i+1][j]+a[i][j-1]+a[i][j+1]); f[i][j][0] = min(f[i][j][0],f[i-1][j][1]+a[i+1][j]+a[i][j+1]); f[i][j][1] = min(f[i][j][1],f[i][j-1][1]+a[i-1][j]+a[i+1][j]+a[i][j+1]); f[i][j][1] = min(f[i][j][1],f[i][j-1][0]+a[i][j+1]+a[i+1][j]); } } printf("%d\n",min(f[n][m][1],f[n][m][0])); return 0; }
C. 道路和航线
题目描述
Farmer John 正在一个新的销售区域对他的牛奶销售方案进行调查。他想把牛奶送到 T个城镇 ,编号为 1到T 。这些城镇之间通过R 条道路(编号为1 到 R)和 条航线(编号为 1到 P)连接。每条道路 i或者航线i 连接城镇Ai 到 Bi,花费为Ci 。
对于道路0<=Ci<=104,然而航线的花费很神奇,花费Ci可能是负数。道路是双向的,可以从Ai到Bi,也可以从Bi到Ai,话费都是Ci,然而航线与之不同,只可以Ai到Bi。
事实上,由于最近恐怖主义太嚣张,为了社会和谐,出台了一些政策保证:如果有一条航线可以从Ai到Bi,那么保证不可能通过一些道路和航线从Bi回到Ai。由于 FJ 的奶牛世界公认十分给力,他需要运送奶牛到每一个城镇。他想找到从发送中心城镇S把奶牛送到每个城镇的最便宜的方案,或者知道这是不可能的。
输入格式
第一行为四个空格隔开的整数:T,R,P,S;
第二到第R+1行:三个空格隔开的整数(表示一条道路):Ai,Bi和Ci;
第R+2到R+P+1行:三个空格隔开的整数(表示一条航线):Ai,Bi和Ci。
输出格式
输出T行,第i行表示到达城镇i的最小花费,如果不存在输出NO PATH。
样例
样例输入
6 3 3 4 1 2 5 3 4 5 5 6 10 3 5 -100 4 6 -100 1 3 -10
样例输出
NO PATH NO PATH 5 0 -95 -100
数据范围与提示
对于全部数据,1<=T<=2.5×104,1<=R,P<=5×104,1<=Ai,Bi,S<=T,保证对于所有的道路,0<=Ci<=104,对于所有的航线,-104<=Ci<=104.
分析
这到题就是简单的最短路是吧,但是会用spfa会TLE,所以我们用一个双端队列进行优化,(会卡过900多毫秒)。
Code
#include<bits/stdc++.h> using namespace std; const int N = 1e6; int t,r,p,s; int x,y,w; int dis[N],head[N],cnt; bool vis[N]; deque<int>q; struct edge{ int to; int ne; int w; }e[N]; void add(int u,int v,int w){ e[++cnt].to = v; e[cnt].ne = head[u]; e[cnt].w = w; head[u] = cnt; } void spfa(int s){ memset(dis,0x3f,sizeof(dis)); q.push_back(s); vis[s] = 1; dis[s] = 0; while(!q.empty()){ int f = q.front(); q.pop_front(); vis[f] = 0; for(int i = head[f];i;i = e[i].ne){ int v = e[i].to; if(dis[v] > dis[f] + e[i].w){ dis[v] = dis[f] + e[i].w; if(!vis[v]){ if(!q.empty()&&dis[v]>=dis[q.front()])q.push_back(v); else q.push_front(v); vis[v] = 1; } } } } } int main(){ scanf("%d%d%d%d",&t,&r,&p,&s); for(int i = 1;i <= r;i++){ scanf("%d%d%d",&x,&y,&w); add(x,y,w); add(y,x,w); } for(int i = 1;i <= p;i++){ scanf("%d%d%d",&x,&y,&w); add(x,y,w); } spfa(s); for(int i = 1;i <= t;i++){ if(dis[i] == 0x3f3f3f3f)printf("NO PATH\n"); else printf("%d\n",dis[i]); } return 0; }
D. 数列运算
题目描述
在纸上有一个长为n的数列,第i项值为ai。
现在小A想要在这些数之间添加加号或乘号。问对于不同的
种方案,
所有答案的和是多少?
由于数据范围较大,所以输出对1000000007取模的结果。
输入格式
输入第一行一个整数n表示数列的长度。
之后一行n个整数,第n个整数表示数列的第i项ai。
输出格式
一行,答案对1000000007取模的结果。
样例
样例输入
3 1 2 4
样例输出
30
数据范围与提示
于30%的数据,1≤n≤10,1≤ai≤10^5
对于另外30%的数据,1≤n≤1000,ai=1
对于90%的数据,1≤n≤1000,1≤ai≤10^5
对于100%的数据,1≤100000,1≤ai≤10^9
分析
初步判断这到题就是来捣乱的。
Code
#include<bits/stdc++.h> using namespace std; #define ll long long const int maxn = 1e5+10; const ll mod = 1000000007; ll n; ll a[maxn],f[maxn],sum[maxn],s[maxn]; ll pai[maxn]; ll quickpow(ll a,ll b){ ll ans = 1; while(b){ if(b&1)ans = ans%mod*a%mod%mod; a=a%mod*a%mod; b>>=1; } return ans%mod; } int main(){ scanf("%lld",&n); scanf("%lld",&a[1]); pai[0]=1; pai[1]=a[1]; for(int i=2;i<=n;++i){ scanf("%lld",&a[i]); pai[i] = pai[i-1]%mod*a[i]%mod; s[i] = s[i-1]%mod*a[i]%mod+a[i]%mod*quickpow(2,i-2)%mod; } f[1]=a[1]; sum[1]=f[1]; for(int i=1;i<=n;++i){ f[i] = sum[i-1]%mod+pai[i]%mod+s[i]%mod; sum[i] = sum[i-1]%mod+f[i]%mod; } printf("%lld\n",f[n]%mod); return 0; }