DAY1DAY1DAY1
T1T1T1
打工打工打工
Input
第一行,一个整数N表示参赛人数。
第二行,N个整数,表示询问的分队方式的序列。
Output
一行,一个整数表示这种方式会在第几天被采用。答案对1,000,007取模。
Sample Input
3
1 2 2
Sample Output
4
Data Constraint
对于100%的数据,N ≤ 10000 , 数据保证询问的数列是一个有效的序列。
详细情况见下表。
Hint
比赛各天的分队情况如下:
第一天:1,1,1
第二天:1,1,2
第三天:1,2,1
第四天:1,2,2
第五天:1,2,3
考场遇到这道题时起先想到暴力枚举所有情况然后判断在第几次出现,可是发现好像连如何枚举都很麻烦,开始思考。考场时发现无论如何你当前所填的那个空一定小于等于已填空中的最大值加1,显然。不过时间原因没打暴力,也未作深入思考。
靠后理解良久后看懂了题解,大概思路如下。
设f[i][j]f[i][j]f[i][j]表示前i个数中最大的数为j时的方案数,首先时比较显然的转移
f[i][j]=f[i−1][j]∗j+f[i−1][j−1]f[i][j] = f[i - 1][j] * j + f[i - 1][j - 1]f[i][j]=f[i−1][j]∗j+f[i−1][j−1]
当前i−1i - 1i−1个数的最大值已经是jjj了,那么对于第iii空有jjj种填法,而当前i−1i - 1i−1个数的最大值是j−1j - 1j−1时,那么对于第iii空只有一种填法即填jjj。详见Code:
#include <cstdio>
#include <cstring>
#define ll long long
#define mo 1000007
using namespace std;
const int maxm = 1e4 + 10;
ll ans,f[2][maxm],n,a[maxm],max;
int p;
int main()
{
scanf("%lld",&n);
for (ll i = 1; i <= n; i ++) scanf("%lld",&a[i]);
max = a[1];
p = 1;
for (ll i = 1; i <= n; i ++)
{
memset(f[p],0,sizeof (f[p]));
for (ll j = 1; j <= i; j ++)
f[p][j] = (f[1 ^ p][j] * j + f[1 ^ p][j - 1]) % mo;
f[p][max] = (f[p][max] + a[i] - 1) % mo;
max = a[i] > max ? a[i] : max;
p ^= 1;
}
for (ll j = 1; j <= n; j ++) ans = (ans + f[1 ^ p][j]) % mo;
ans = (ans + 1) % mo;
printf("%lld",ans);
return 0;
}
其中有一处细节也是重点需要注意f[p][max]=(f[p][max]+a[i]−1) mod mof[p][max] = (f[p][max] + a[i] - 1) \ mod \ mof[p][max]=(f[p][max]+a[i]−1) mod mo,该意思为如题目给出序列为1 2 3 3 4,那么当你前三个都已经满足1 2 3时第四个空当前只能填1或2。
T2T2T2
破解破解破解
Input
第一行,一个整数N表示参赛人数。
第二行,N个整数,表示询问的分队方式的序列。
Output
一行,一个整数表示这种方式会在第几天被采用。答案对1,000,007取模。
Sample Input
2
3 3
1 1
2 2
3 3
5 2
1 2
4 5
Sample Output
8
4
Data Constraint
对于30%的数据,N,M ≤ 10
对于60%的数据,N ≤ 10000000 , M ≤ 20
对于100%的数据,N ≤ 10000000 , M ≤ 100000 ,1 ≤ Li ≤ Ri ≤ N , T ≤ 10
Hint
第一组数据:每个位置都可以单个修改,所以所有长度为3的01串都有可能,即2^3=8种可能。
第二组数据的四种可能如下:
1.不操作:00000
2.选择区间[1,2]:11000
3.选择区间[4,5]:00011
4.先选择区间[1,2]再选择[4,5]:11011
考场耗时最长的一道题,可是爆零。想到题目意思就是将Li−RiL_i - R_iLi−Ri区间内的数都异或1,那么很显然异或偶数次则恢复原值,我便陷入了错误的思想,一直死磕如何用异或的性质完成这道题,虽与正解思路有那么几分相近可还是没想到。
正解思路将每个数与其前面的数字异或,那么很容易发现其实Li,Ri+1L_i,R_{i + 1}Li,Ri+1两个点的值发生了改变,所以将Li,Ri+1L_i,R_{i + 1}Li,Ri+1两个点连边,然后加入它们已在同一个联通块中那么对答案没有贡献,否则答案*2。
至于将Li,Ri+1L_i,R_{i + 1}Li,Ri+1两个点连边而不是将Li,RiL_i,R_iLi,Ri连边我并不能十分理解,只能通过举特殊例子来说明将Li,RiL_i,R_iLi,Ri连边的错误性,比如一下这组数据。
1
4 3
1 2
3 4
1 4
答案为4,可是假如按照Li,RiL_i,R_iLi,Ri连边则答案为8。Li,Ri+1L_i,R_{i + 1}Li,Ri+1两个点连边就是为了将这个数列可以关联起来避免交界处的错误。
AC Code:
#include <cstdio>
using namespace std;
const int mo = 1e9 + 7;
const int maxn = 1e7 + 10;
const int maxm = 1e5 + 10;
int n,m,T,fa[maxn],l[maxm],r[maxm],ans;
int read()
{
int x = 0,w = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') w = -1;ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0';ch = getchar();}
return x * w;
}
int get(int a)
{
if (a == fa[a]) return a; else return fa[a] = get(fa[a]);
}
int main()
{
T = read();
while (T --)
{
ans = 1;
n = read(),m = read();
for (int i = 1; i <= m; i ++) l[i] = read(),r[i] = read() + 1,fa[l[i]] = l[i],fa[r[i]] = r[i];
for (int i = 1,x,y; i <= m; i ++)
{
x = get(l[i]),y = get(r[i]);
if (x != y)
{
ans = (ans * 2) % mo;
fa[x] = y;
}
}
for (int i = 1; i <= m; i ++) fa[l[i]] = fa[r[i]] = 0;
printf("%d\n",ans);
}
return 0;
}
T3T3T3
书稿书稿书稿
Input
第一行,两个整数W,H,表示网格图的大小。
第二行,一个整数N,表示墨水的数量
接下来N行,第i行两个整数xi,yi,ai,bi表示一滴落在(xi,yi)的墨水和它的参数(ai,bi)。
接下来一行,一个整数Q,表示询问的个数。
再接下来Q行,每行四个整数x1j,y1j,x2j,y2j,表示询问的子网格图的左上角为(x1j,y1j)右下角为(x2j,y2j)。
Output
每个询问一行,一个整数表示询问的子网格图的平均受损值,答案四舍五入。
Sample Input
4 3
2
1 1 7 3
3 2 4 2
4
1 2 2 3
1 1 4 3
4 2 4 2
1 3 4 3
Sample Output
4
4
2
2
Data Constraint
对于100%的数据满足:
W*H ≤ 2500000 , N,Q ≤ 200000 , 1 ≤ xi ≤ W , 1 ≤ yi ≤ H , 1 ≤ ai,bi ≤ 10^9 ,1 ≤ x1j ≤ x2j ≤ W , 1 ≤ y1j ≤ y2j ≤ H
数据保证最后所有的受损值之和小于2^63。
为方便描述,补充定义“边界点”:如果一滴墨水,存在网格图外的一点受到它的影响大于0,那么我们就称这滴墨水是一个边界点。
详细数据约束见下表。
Hint
最终每个网格的受损值如下图:
第一个询问受损值之和是14,平均值是14/4=3.5,四舍五入是4
第二个询问受损值之和是44,平均值是44/12=3.666…,四舍五入是4
第三个询问受损值之和是2,平均值是2/1=2
第四个询问受损值之和是9,平均值是9/4=2.25,四舍五入是2
考场感觉这是一道十分不简单的题目,考完发现果然如此。考场想打H = 1的暴力以获取36分,可是不知道为什么打炸了。这题基本思路是二维差分加二维前缀和,暂未思考出正解。
一个月没打题果然状态急速下降,0 + 0 + 4,考试仅得4分,加快调整好状态。