CSP-S模拟5
A. F
当时看见数据范围的时候想了半天为什么只给了n的范围,我还需要ai <= 1e7的部分分,后来才发现正解就是枚举两层n,(由于数据比较水,我从0到1<<位数 枚举异或的结果也可以过),然而我没有清空set !导致内存炸了MLE 10(因为用暴力保底了,事实上枚举位数的做法只要有一个1e9的数,n=3在本地都跑不出来) 否则Cat总分就上200了啊
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2004;
const int N = 505;
const int INF = 1e9;
int n, a[maxn], b[maxn], mx, Max, cnt, cmx, Min;
vector<int> ans;
multiset<int> s, sp;
multiset<int>::iterator it;
set<int> st;
set<int>::iterator kit;
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
bool check(int num)
{
for(int i=1; i<=n; i++)
{
int x = num^a[i];
if(s.find(x) == s.end()) return 0;
s.erase(s.find(x));
sp.insert(x);
}
return 1;
}
void check2()
{
int x = a[1] ^ b[1];
for(int i=2; i<=n; i++)
{
if((a[i]^b[i]) != x) return;
}
st.insert(x);
}
int main()
{
freopen("f.in", "r", stdin);
freopen("f.out", "w", stdout);
n = read();
if(n <= 10)
{
for(int i=1; i<=n; i++)
{
a[i] = read();
}
for(int i=1; i<=n; i++)
{
b[i] = read();
}
sort(b+1, b+1+n);
do
{
check2();
}while(next_permutation(b+1, b+1+n));
int sz = st.size();
printf("%d\n", sz);
for(kit=st.begin(); kit!=st.end(); kit++)
{
int x = *kit;
printf("%d ", x);
}
printf("\n");
exit(0);
}
for(int i=1; i<=n; i++)
{
a[i] = read(); mx = max(a[i], mx);
}
for(int i=1; i<=n; i++)
{
b[i] = read(); mx = max(b[i], mx);
s.insert(b[i]);
}
while(mx)
{
mx >>= 1; cnt++;
}
Max = (1 << cnt);
for(int i=0; i<Max; i++)
{
if(s.size() > sp.size())
{
for(it=sp.begin(); it!=sp.end(); it++)
{
s.insert(*it);
}
}
else
{
for(it=s.begin(); it!=s.end(); it++)
{
sp.insert(*it);
}
swap(sp, s);
}
sp.clear();
if(check(i))
{
ans.push_back(i);
}
}
sort(ans.begin(), ans.end());
int sz = ans.size();
printf("%d\n", sz);
for(int i=0; i<sz; i++)
{
printf("%d ", ans[i]);
}
printf("\n");
return 0;
}
B. S
乱搞了一下,我都不知道它为什么能有40分,前面的部分大概有一点依据,就是在一段相同颜色之后如果有一个不同的颜色,那就把它往前移,然而如果它以连续段为结尾的话,那就没解了,但其实样例告诉我们可以用前面的来填补后面,于是我的想法是错的,乱搞的成分就在于直接加上了后面连续段的长度-1。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 404;
int a[maxn], n, ans, las, l, r;
char s[maxn];
inline int read()
{
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
bool check()
{
int bok = s[1]-'A', cnt = 1;
for(int i=2; i<=n; i++)
{
if(bok == s[i]-'A') cnt++;
else
{
cnt--;
if(cnt < 0) bok = s[i]-'A', cnt = 1;
}
}
cnt = 0;
for(int i=1; i<=n; i++)
{
if(bok == s[i]-'A') cnt++;
else cnt--;
}
if(cnt <= 1) return 0;
else return 1;
}
int main()
{
freopen("s.in", "r", stdin);
freopen("s.out", "w", stdout);
n = read();
scanf("%s", s+1);
if(check())
{
printf("-1\n"); exit(0);
}
for(int i=1; i<=n; i++)
{
if(s[i] == 'R') a[i] = 1;
else if(s[i] == 'G') a[i] = 2;
else a[i] = 3;
}
/*for(int i=1; i<=n; i++) printf("%d ", a[i]);
printf("\n");*/
las = a[1]; l = r = 1;
for(int i=2; i<=n; i++)
{
//printf("i = %d l = %d r = %d\n", i, l, r);
if(a[i] != las)
{
if(r-l > 0)
{
ans += r-l, swap(a[l+1], a[i]);
/*printf("l = %d r = %d\n", l, r);
for(int j=1; j<=n; j++) printf("%d ", a[j]);
printf("\n");*/
i = l = r = l+2; las = a[i]; continue;
}
l = r = i; las = a[i];
}
else r++;
}
//printf("l = %d r = %d\n", l, r);
for(int i=r-l; i>0; i--)
{
ans += i;
}
//ans += r-l;
printf("%d\n", ans);
return 0;
}
正解果然是动态规划!
考虑枚举最后的序列是什么,如果要交换的话同种颜色的相对顺序不会变。如果知道3种颜色分别填了多少,再加上预处理出原序列中1颜色填特定个数对应的另外两种颜色填上的个数,就可以得到当前状态的逆序对数,逆序指的违背了是颜色间的相对顺序,这个逆序对数就是交换次数。
其实不需要用摩尔投票来判断合法性,开个桶直接判断就行因为我知道元素只有这3种;其实也可以不判断,数据点里根本就没有!
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 202;
const int N = 505;
const int INF = 1e9;
int n, f[maxn][maxn][maxn][3], pos[3];
pair<int, int> pre[3][maxn];
char s[402];
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
bool check()
{
int bok = s[1]-'A', cnt = 1;
for(int i=2; i<=n; i++)
{
if(bok == s[i]-'A') cnt++;
else
{
cnt--;
if(cnt < 0) bok = s[i]-'A', cnt = 1;
}
}
cnt = 0;
for(int i=1; i<=n; i++)
{
if(bok == s[i]-'A') cnt++;
else cnt--;
}
if(cnt <= 1) return 0;
else return 1;
}
int main()
{
freopen("s.in", "r", stdin);
freopen("s.out", "w", stdout);
n = read();
scanf("%s", s+1);
if(check())
{
printf("-1\n"); exit(0);
}
for(int i=1; i<=n; i++)
{
if(s[i] == 'R') pos[0]++, pre[0][pos[0]] = make_pair(pos[1], pos[2]);
else if(s[i] == 'G') pos[1]++, pre[1][pos[1]] = make_pair(pos[0], pos[2]);
else pos[2]++, pre[2][pos[2]] = make_pair(pos[0], pos[1]);
}
memset(f, 0x3f, sizeof(f));
if(pos[0]) f[1][0][0][0] = 0;
if(pos[1]) f[0][1][0][1] = 0;
if(pos[2]) f[0][0][1][2] = 0;
for(int a=0; a<=pos[0]; a++)
{
for(int b=0; b<=pos[1]; b++)
{
for(int c=0; c<=pos[2]; c++)
{
for(int i=0; i<=2; i++)
{
if(i==0 && a) f[a][b][c][0] = min(f[a][b][c][0], min(f[a-1][b][c][1], f[a-1][b][c][2])+max(0, b-pre[0][a].first)+max(0, c-pre[0][a].second));
if(i==1 && b) f[a][b][c][1] = min(f[a][b][c][1], min(f[a][b-1][c][0], f[a][b-1][c][2])+max(0, a-pre[1][b].first)+max(0, c-pre[1][b].second));
if(i==2 && c) f[a][b][c][2] = min(f[a][b][c][2], min(f[a][b][c-1][0], f[a][b][c-1][1])+max(0, a-pre[2][c].first)+max(0, b-pre[2][c].second));
}
}
}
}
printf("%d\n", min(f[pos[0]][pos[1]][pos[2]][0], min(f[pos[0]][pos[1]][pos[2]][1], f[pos[0]][pos[1]][pos[2]][2])));
return 0;
}
C. Y
暴力的话,就枚举一下每个人给出的球的序列,然后hash一下去个重,由于针对的数据太小不用考虑hash溢出的问题,可以直接转成10进制扔进map里。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 21;
const int N = 505;
const ll mod = 1e9 + 7;
int n, a[maxn], b[maxn], d[maxn];
ll sum, ans, num;
map<ll, int> mp;
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
inline bool nt()
{
for(int i=1; i<=n; i++)
{
if(b[i]) return 0;
}
return 1;
}
inline void check()
{
//if(nt()) return;
/*printf("New case:\n");
for(int i=1; i<=n; i++) printf("%d ", b[i]);
printf("\n");*/
for(int i=1; i<=n; i++) d[i] = a[i];
for(int i=1; i<n; i++)
{
d[i] -= b[i]; d[i] += b[i+1];
}
d[n] -= b[n]; d[n] += b[1];
ll h = 0;
for(int i=1; i<=n; i++)
{
h = (h<<1)+(h<<3)+d[i];
}
if(mp.find(h) == mp.end())
{
mp[h] = ++num;
}
else return;
/*for(int i=1; i<=n; i++) printf("%d ", d[i]);
printf("\n");*/
sum = 1;
for(int i=1; i<=n; i++)
{
sum = sum * d[i] % mod;
}
//if(sum) printf("sum = %lld\n", sum);
ans = (ans + sum) % mod;
}
void dfs(int p, int n)
{
if(p > n)
{
check();
return;
}
for(int i=0; i<=a[p]; i++)
{
b[p] = i;
dfs(p+1, n);
}
}
int main()
{
freopen("y.in", "r", stdin);
freopen("y.out", "w", stdout);
n = read();
for(int i=1; i<=n; i++) a[i] = read();
dfs(1, n);
printf("%lld\n", ans);
return 0;
}
首先考虑什么情况下两种不同的传递方案可以得到相同的结果:当每个人传给下一个人球(ci)的个数都大于0时,让每个人少传一个,答案序列不会改变,所以只需要考虑至少一个人不传球的情况。
至少有一个人是不传球的方案数:总方案数 - 每个人都传球的方案数
——连乘从1到n (a[i]+1) - 连乘a[i]
由于这是一个环,所以还要讨论1是从自己转移还是从n转移,分开讨论最后再加起来。
把这两段连乘拆开,就是把前后两项分开算。其中一项可以理解成每个人传完球后从手里剩下的球中选一个的方案数,两次dp作差就好,可以在函数里传个参数把每个a[i]都预先-1得到后一项。
又是奇奇妙妙的动态规划:f[i][0] 表示第i个人从自己原有的球上选一个,这时第i-1个人还没有把球传给i。f[i][1] 表示第i个人从第i-1个人传来的球中选一个,这时显然i-1已经把球给了i。第i-1个人表示上一个人也就是i右边的人。i既可以从自己原有的球中选也可以从i-1传过来的球中选,所以要累加。

至于这个g(i)函数的计算公式 ∑(1~n) i^2 = n * (n+1) * (2*n+1) / 6。我不知道它是怎么被发现的,但是可以证明它是对的。
当n=1时,12=1=1*2*3/6,命题成立;
当n=k时,假设∑k(i=1)i2=k(k+1)(2k+1)/6成立,当k=n+1时,就可以代入——
∑n(i=1)i^2=∑k(i=1)i^2+(k+1)^2
=(k+1)*[1/6*k*(2*k+1)+(k+1)]
=(k+1)*[1/3*k^2+1/6*k+k+1]
=(k+1)*(1/3*k^2+7/6*k+1)
=1/6*(k+1)(2*(k^2)+7k+6)
=1/6*(k+1)*(k+2)*(2k+3)
=1/6*(k+1)*[(k+1)+1]*[2*(k+1)+1]
递推可得正确性。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
const int N = 505;
const int mod = 1e9 + 7;
const int iv3 = (mod+1)/3;//1+8=9,它恰好可以被3整除!
int n, a[maxn], f[maxn][2];
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
int calc(int x, int y)
{
memset(f, 0, sizeof(f));
f[0][x] = 1;
for(int i=0; i<n; i++)
{
int g = a[i]-y, c = (g+1ll)*g/2%mod;
f[i+1][0] = (1ll*c*f[i][0]+(g+1ll)*f[i][1])%mod;
g += y; c = (g+1ll)*g/2%mod;
//就是上文的式子,你发现a[i]*s(a[i])-g(a[i])可以乘法结合律,提出来一个s(a[i])
int ss = c*(g-1ll)%mod*iv3%mod;
f[i+1][1] = (1ll*c*f[i][1]+1ll*ss*f[i][0])%mod;
}
return f[n][x];
}
int main()
{
freopen("y.in", "r", stdin);
freopen("y.out", "w", stdout);
n = read();
for(int i=0; i<n; i++) a[i] = read();
printf("%lld\n", ((ll)calc(0,0)+calc(1,0)-calc(0,1)-calc(1,1)+2ll*mod)%mod);
return 0;
}
D. O
我只会模拟 TLE 30,还不会改呢qwq
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 7;
const int N = 505;
const int INF = 1e9;
int n, m, now, tot;
ll a[maxn], ans[maxn], d[maxn];
bool fl;
inline ll read()
{
ll x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9')
{
if(ch == '-')
{
f = -1;
}
ch = getchar();
}
while(ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch^48);
ch = getchar();
}
return x * f;
}
struct node
{
int t, l, r, id;
bool operator < (const node &T) const
{
return t < T.t;
}
}q[maxn];
int main()
{
freopen("o.in", "r", stdin);
freopen("o.out", "w", stdout);
n = read(); m = read();
for(int i=1; i<=n; i++) a[i] = read();
for(int i=1; i<=m; i++)
{
q[i].t = read(); q[i].l = read(); q[i].r = read();
q[i].id = i;
}
sort(q+1, q+1+m);
for(int i=1; i<=m; i++)
{
int t = q[i].t, l = q[i].l, r = q[i].r;
while(now < t && !fl)
{
now++; tot = 0;
for(int j=1; j<=n; i++) d[j] = a[j];
for(int j=2; j<=n; j++)
{
if(d[j-1] > a[j]) a[j] = d[j-1], tot++;
}
if(tot == 0) fl = 1;
}
for(int j=l; j<=r; j++)
{
ans[q[i].id] += a[j];
}
}
for(int i=1; i<=m; i++)
{
printf("%lld\n", ans[i]);
}
return 0;
}

浙公网安备 33010602011771号