比赛过的(3) | 补题(1) |
---|---|
B D F | A |
当 2 ∗ r ≥ b 2 * r \ge b 2∗r≥b的时候掉下去
若掉不下去
延长梯形随便推推
#include
#include
#include
#include
#include
using namespace std;
double r, a, b, h;
int main()
{
cin >> r >> a >> b >> h;
if(2 * r <= b) puts("Drop");
else {
puts("Stuck");
double alhpa = atan((a - b) / (2 * h));
double h1 = b / (2 * tan(alhpa)), t = r * cos(alhpa), h3 = r * sin(alhpa);
double h2 = t / tan(alhpa);
double ans = h3 + h2 - h1;
printf("%.10f\n", ans);
}
return 0;
}
给你一个矩阵,问你有多少个地方有连续的 x 个 0,一定要在一行中。
#include
using namespace std;
int n, m, a[2001][2001], b[2001], ans;
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
int num = 0;
for (int j = 1; j <= n; j++) {
scanf("%1d", &a[i][j]);
if (a[i][j] == 0) num++;
else num = 0;
if (num >= m) ans++;
}
}
printf("%d", ans);
return 0;
}
题意寻找l到r区间中的一个数x连续子序列 m o d 3 = 0 \mod3=0 mod3=0的个数( 1 ≤ l ≤ r ≤ 1 e 18 1\le l\le r \le1e18 1≤l≤r≤1e18)
大于100的都有,小于100的枚举。
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int T, id;
ll l, r;
int ans[105];
void init()
{
for(int i = 1;i <= 9;i++) if(i % 3 == 0) ans[id++] = i;
for(int i = 10;i <= 99;i++)
{
if(i % 3 == 0 || (i % 10) % 3 == 0 || ((i / 10) % 3 == 0))
ans[id++] = i;
}
}
int main()
{
init();
scanf("%d", &T);
while(T--)
{
scanf("%lld%lld", &l, &r);
ll res;
if(r < 100) {
res = upper_bound(ans, ans + id, r) - lower_bound(ans, ans + id, l);
}
else if(l >= 100) res = r - l + 1;
else res = (r - 100 + 1) + upper_bound(ans, ans + id, 100) - lower_bound(ans, ans + id, l);
printf("%lld\n", res);
}
return 0;
}
从 f [ 0 ] [ 0 ] f[0][0] f[0][0]开始枚举,此时是必败态, f [ i + k ] [ j + s ∗ k ] = 1 f[i + k][j + s * k] = 1 f[i+k][j+s∗k]=1 且 f [ i + s ∗ k ] [ j + k ] = 1 f[i + s * k][j + k] = 1 f[i+s∗k][j+k]=1
枚举之后下一个状态是必胜态
正解需要打表
#include
#include
#include
#include
using namespace std;
int T;
bool f[5001][5001];
void init()
{
for(int i = 0; i <= 5000; i++)
{
for(int j = 0; j <= 5000; j++)
{
if(f[i][j] == 0)
{
for(int k = 1; k + i <= 5000; k++)
{
for(int s = 0; j + s * k <= 5000; s++)
{
f[i + k][j + s * k] = 1;
}
}
for(int k = 1; j + k <= 5000; k++)
{
for(int s = 0; i + s * k <= 5000; s++)
{
f[i + s * k][j + k] = 1;
}
}
}
}
}
}
int main()
{
init();
scanf("%d", &T);
while(T--)
{
int n, m;
scanf("%d%d", &n, &m);
if(f[n][m] == 1) puts("Alice");
else puts("Bob");
}
return 0;
}
比赛过的(3) | 补题(2) |
---|---|
C D F | K I |
签到,注意数据范围
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int n, m;
int main()
{
scanf("%d%d",&n,&m);
if(n % 2 == 1 && m % 2 == 1) puts("NO");
else puts("YES");
return 0;
}
签到
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int T;
bool cmp(int a1, int b1, int a2, int b2)
{
if(a1 > b1) swap(a1, b1);
if(a2 > b2) swap(a2, b2);
//printf("a1=%d b1=%d a2=%d b2=%d\n",a1,b1,a2,b2);
if(a1 == 2 && b1 == 8) return true;
else if(a2 == 2 && b2 == 8) return false;
else if(a1 == b1 && a2 == b2) return a1 > a2;
else if(a1 == b1) return true;
else if(a2 == b2) return false;
else {
if((a1+b1)%10 == (a2+b2) % 10) return b1 > b2;
else return (a1+b1)%10 > (a2+b2) % 10;
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
int a1, b1, a2, b2;
scanf("%d%d%d%d",&a1,&b1,&a2,&b2);
if(a1 > b1) swap(a1, b1);
if(a2 > b2) swap(a2, b2);
if(a1 == a2 && b1 == b2) puts("tie");
else {
bool flag = cmp(a1,b1,a2,b2);
if(flag) puts("first");
else puts("second");
}
}
return 0;
}
空间集合,求两个球相交部分的体积
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int T;
const double pi = acos(-1);
double x[4], y[4], z[4];
double k1, k2;
double dis(double x1, double y1, double z1, double x2, double y2, double z2)
{
double ans = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)+(z2-z1)*(z2-z1));
return ans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
for(int i = 0;i < 4;i++) {
scanf("%lf%lf%lf",&x[i],&y[i],&z[i]);
}
scanf("%lf%lf",&k1,&k2);
double xc1, yc1, zc1, xc2, yc2, zc2, r1, r2;
xc1 = (k1*k1*x[1]-x[0])/(k1*k1-1);
yc1 = (k1*k1*y[1]-y[0])/(k1*k1-1);
zc1 = (k1*k1*z[1]-z[0])/(k1*k1-1);
xc2 = (k2*k2*x[3]-x[2])/(k2*k2-1);
yc2 = (k2*k2*y[3]-y[2])/(k2*k2-1);
zc2 = (k2*k2*z[3]-z[2])/(k2*k2-1);
r1 = k1 * dis(x[1],y[1],z[1],x[0],y[0],z[0]) / (k1*k1-1);
r2 = k2 * dis(x[3],y[3],z[3],x[2],y[2],z[2]) / (k2*k2-1);
double rmax = max(r1,r2), rmin = min(r1,r2);
double res = 0;
double d = (dis(xc1,yc1,zc1,xc2,yc2,zc2));
if(d >= r1 + r2) res = 0;
else if(d + rmin <= rmax) res = (4.0/3)*pi*rmin*rmin*rmin;
else {
double co = (rmax*rmax+d*d-rmin*rmin) / (2.0*d*rmax);
double h = rmax * (1-co);
res += (1.0/3)*pi*(3.0*rmax-h)*h*h;
co = (rmin * rmin + d*d -rmax*rmax) / (2.0*d*rmin);
h = rmin*(1-co);
res += (1.0/3)*pi*(3.0*rmin-h)*h*h;
}
printf("%.10f\n",res);
}
return 0;
}
题意:给出单调栈中数列前几个元素的size,求可能的序列。
在数组 b b b中,每个给出的 b i b_i bi前一定有一段 1 , 2 , … , b i − 1 1, 2, \dots, b_{i - 1} 1,2,…,bi−1的子序列,不妨把这段子序列连续地放在 b i b_i bi前,之后判断构造出的数组 b b b否合法。
将b数组补全,数组中元素相邻不能跳跃递增,补全之后进行排序即可,直接进行模拟。
数组 b 合法即 b 0 = 1 b_0 = 1 b0=1 且不存在两个相邻的数后者与前者之差大于 1 。
之后,考虑 1 的位置,因为 1 是最小的,每次出现一定会将栈清空且无法被之后的数清空,所以 1 一定在最右的$ b_i = 1$ 处。
接下来考虑 2 的位置,因为此时 2 是剩下未使用过的数中最小的数,所以 2 会在次右的 b i = 1 b_i = 1 bi=1 或最右的 b i = 2 b_i = 2 bi=2处。
……
重复以上步骤直至将 n 个数放完。
即,将构造好的数组 b 按值从小到大,位置从右至左的顺序依次赋上 1 , 2 , … , n 1,2,\dots,n 1,2,…,n 即可。
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 1e6+50;
int n, k;
struct node{
int p, x;
}N[MAXN];
int a[MAXN];
bool vis[MAXN];
int b[MAXN];
vectorv;
bool cmp(node a, node b)
{
if(a.x == b.x) return a.p > b.p;
else return a.x < b.x;
}
int main()
{
scanf("%d%d",&n,&k);
while(k--)
{
int p, x;
scanf("%d%d",&p,&x);
v.push_back({p,x});
b[p] = x;
}
int tmp = 0;
bool flag = true;
for(int i = 1;i <= n;i++)
{
if(!b[i]) {
b[i] = ++tmp;
v.push_back({i,tmp});
}
else {
if(b[i] - b[i-1] > 1) {
flag = false;
break;
}
tmp = b[i];
}
}
sort(v.begin(),v.end(),cmp);
/*
for(int i = 0;i < v.size();i++)
{
printf("%d %d\n",v[i].p,v[i].x);
}
*/
int cnt = 1;
for(int i = 0;i < v.size();i++)
{
a[v[i].p] = cnt;
cnt++;
}
if(!flag) puts("-1");
else{
for(int i = 1;i <= n;i++)
{
printf("%d ",a[i]);
}
printf("\n");
}
return 0;
}
题意:给你一个地图,通过控制两个企鹅从起点((20,20),(20,1))到终点((1,20),(1,1))且两个企鹅镜像移动(及同时上下,左右相反),当一边碰到障碍或边界,是不会移动的,所以可能只有一侧移动,求最少的步数和路径图;
思路:bfs+路径记录
1.创建一个四维数组来记录;
2.两个企鹅只要有一只可以移动就继续进行;
3.在队列中创建一个string来存储路径;
就是一个简单的BFS,使用一个四维数组$v i s [ x 1 ] [ y 1 ] [ x 2 ] [ y 2 ] $[y2]记录当前位置( x 1 , y 1 ) , ( x 2 , y 2 )
的状态,然后按照字典序遍历当前点四周位置,合法则继续走,不合法跳过,走到终点就停止。
#include
#include
#include
#include
#include
#include
using namespace std;
char mp1[45][45], mp2[45][45], ans1[45][45], ans2[45][45];
bool vis[45][45][45][45];
int res;
string s;
struct node{
int x1, y1, x2, y2, cnt;
char c;
}p[45][45][45][45];
char rou[4] = {'D', 'L', 'R', 'U'};
int pg1[4][2] = {1, 0, 0, -1, 0, 1, -1, 0};
int pg2[4][2] = {1, 0, 0, 1, 0, -1, -1, 0};
queueq;
bool check1(int x1, int y1)
{
if(x1 < 1 || x1 > 20 || y1 < 1 || y1 > 20 || mp1[x1][y1] == '#') return false;
return true;
}
bool check2(int x2, int y2)
{
if(x2 < 1 || x2 > 20 || y2 < 1 || y2 > 20 || mp2[x2][y2] == '#') return false;
return true;
}
void bfs()
{
memset(vis, false, sizeof(vis));
memset(p, 0x3f, sizeof(p));
while(!q.empty()) q.pop();
p[20][20][20][1] = {20, 20, 20, 1, 0};
q.push({20, 20, 20, 1, 0});
vis[20][20][20][1] = true;
while(!q.empty())
{
auto u = q.front();
q.pop();
//printf("%d %d %d %d %d\n", u.x1, u.y1, u.x2, u.y2, u.cnt);
int x1 = u.x1, y1 = u.y1, x2 = u.x2, y2 = u.y2, cnt = u.cnt;
for(int i = 0;i < 4;i++)
{
int px1 = x1 + pg1[i][0], py1 = y1 + pg1[i][1], px2 = x2 + pg2[i][0], py2 = y2 + pg2[i][1];
char c = rou[i];
// 如果两个都能走
if(check1(px1, py1) && check2(px2, py2))
{
if(p[px1][py1][px2][py2].cnt > cnt + 1 && !vis[px1][py1][px2][py2])
{
p[px1][py1][px2][py2] = {x1, y1, x2, y2, cnt+1, c};
q.push({px1, py1, px2, py2, cnt + 1});
vis[px1][py1][px2][py2] = true;
}
}
//如果只有企鹅一能走
else if(check1(px1, py1) && !check2(px2, py2))
{
px2 = x2, py2 = y2;
if(p[px1][py1][px2][py2].cnt > cnt + 1 && !vis[px1][py1][px2][py2])
{
p[px1][py1][px2][py2] = {x1, y1, x2, y2, cnt+1, c};
q.push({px1, py1, px2, py2, cnt + 1});
vis[px1][py1][px2][py2] = true;
}
}
//如果只有企鹅二能走
else if(!check1(px1, py1) && check2(px2, py2))
{
px1 = x1, py1 = y1;
if(p[px1][py1][px2][py2].cnt > cnt + 1 && !vis[px1][py1][px2][py2])
{
p[px1][py1][px2][py2] = {x1, y1, x2, y2, cnt+1, c};
q.push({px1, py1, px2, py2, cnt + 1});
vis[px1][py1][px2][py2] = true;
}
}
}
}
res = p[1][20][1][1].cnt;
// 模拟路径
int ux1 = 1, uy1 = 20, ux2 = 1, uy2 = 1;
for(int i = 0;i < 4;i++)
{
while(p[ux1][uy1][ux2][uy2].cnt != 0)
{
ans1[ux1][uy1] = 'A';
ans2[ux2][uy2] = 'A';
auto &t = p[ux1][uy1][ux2][uy2];
s += t.c;
ux1 = t.x1, uy1 = t.y1, ux2 = t.x2, uy2 = t.y2;
}
}
ans1[20][20] = 'A', ans2[20][1] = 'A';
reverse(s.begin(), s.end());
return;
}
int main()
{
for(int i = 1;i <= 20;i++)
{
cin >> mp1[i] + 1 >> mp2[i] + 1;
}
memcpy(ans1, mp1, sizeof(mp1));
memcpy(ans2, mp2, sizeof(mp2));
bfs();
printf("%d\n", res);
cout << s << endl;
for(int i = 1;i <= 20;i++)
{
cout << ans1[i] + 1 << ' ' << ans2[i] + 1 << endl;
}
return 0;
}
比赛过的(4) | 补题(0) |
---|---|
1001 1009 1005 1008 |
打表找规律签到
大数据本地就没过 wa了
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int T;
ll qpow(ll a, ll n)
{
ll res = 1;
while(n)
{
if(n & 1) res = res * a;
n >>= 1;
a = a * a;
}
return res;
}
int main()
{
scanf("%d",&T);
//printf("%lld\n",log_2(1e12));
//printf("%lld\n",qpow(2,log_2(1e12)-1)-1);
while(T--)
{
ll n, res;
scanf("%lld",&n);
if(n == 1) res = 0;
else {
ll cnt = 0;
n--;
while(n)
{
n >>= 1;
cnt++;
}
res = qpow(2,cnt-1)-1;
}
printf("%lld\n",res);
}
return 0;
}
排序后从小到大连边,并查集维护连通块个数。
注意边界(最多联通块个数 和 只有一个连通块)
#include
#include
#include
#include
using namespace std;
const int N = 1e5+50, M = 5e5+50;
int T;
int n, m, k;
int fa[N];
struct node{
int a, b, w;
}Node[M];
int find(int x)
{
if(x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
bool cmp(node &a, node &b)
{
return a.w < b.w;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
int cnt = n;
for(int i = 1;i <= n;i++) fa[i] = i;
for(int i = 0;i < m;i++)
{
int a, b, w;
scanf("%d%d%d",&a,&b,&w);
Node[i].a = a;
Node[i].b = b;
Node[i].w = w;
}
sort(Node,Node+m,cmp);
/*
for(int i = 0;i < m;i++)
{
printf("a = %d b = %d w = %d\n",Node[i].a,Node[i].b,Node[i].w);
}
*/
int D = 0;
bool flag = true;
if(n == k) D = 0;
else{
for(int i = 0;i < m;)
{
D = Node[i].w;
int u = find(Node[i].a), v = find(Node[i].b);
//printf("%d %d\n",Node[i].a,Node[i].b);
if(u != v)
{
fa[u] = v;
cnt--;
}
//printf("cnt = %d\n",cnt);
i++;
while(Node[i].w == D && i < m) {
int u = find(Node[i].a), v = find(Node[i].b);
if(u != v) {
fa[u] = v;
cnt--;
}
i++;
//printf("cnt = %d\n",cnt);
}
if(cnt == k) break;
else if(cnt < k) {
flag = false;
break;
}
}
}
/*
for(int i = 1;i <= n;i++) printf("%d ",fa[i]);
printf("\n");
*/
if(!flag || cnt != k) puts("-1");
else printf("%d\n",D);
}
return 0;
}
签到 找规律+素数筛
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 10000010;
int primes[MAXN], cnt, T, n;
bool vis[MAXN];
int main()
{
scanf("%d",&T);
for(int i = 2;i <= MAXN;i++)
{
if(!vis[i]) primes[cnt++] = i;
for(int j = 0;primes[j] * i <= MAXN;j++)
{
vis[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
while(T--)
{
scanf("%d",&n);
ll res = 0;
for(int i = 3;i <= n;i++) {
if(!vis[i]) res += 2*i;
else res += i;
}
printf("%lld\n",res);
}
return 0;
}
题意:求列非递减的最大子矩阵面积
若当前元素比其上一行大则记为1,否则为0.(第一行为0)。
利用单调栈(悬线法)答案
#include
#include
#include
#include
using namespace std;
typedef long long ll;
using namespace std;
int T, n, m;
const int MAXN = 2e3+50;
int a[MAXN][MAXN], b[MAXN][MAXN], h[MAXN], stk[MAXN];
int main()
{
scanf("%d",&T);
while(T--)
{
memset(stk,0,sizeof(stk));
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
scanf("%d",&a[i][j]);
b[i][j] = 0;
if(i > 1 && a[i][j] >= a[i-1][j])
{
b[i][j] = 1;
}
}
}
int ans = 0;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
if(b[i][j] == 0) h[j] = 1;
else h[j]++;
}
h[m+1] = 0;
int tot = 0;
for(int j = 1;j <= m+1;j++)
{
while(tot && h[stk[tot]] > h[j])
{
//printf("j = %d stk[tot-1] = %d h[stk[tot]] = %d ans = %d\n",j,stk[tot-1],h[stk[tot]],(j-stk[tot-1]-1)*h[stk[tot]]);
ans = max(ans,(j-stk[tot-1]-1)*h[stk[tot]]);
tot--;
}
stk[++tot] = j;
}
}
printf("%d\n",ans);
}
return 0;
}
/*
2
4 4
2 2 2 2
1 3 1 1
2 4 0 1
3 5 2 1
4 4
3 3 3 3
2 4 2 2
3 5 1 2
4 6 0 3
*/
题意:给n个数,让最小区间异或和不小于k,若区间长度相等,输出最小的左端点。
字典树需要开的空间等于节点数,而不是需要插入的数的数量,搞错经常会wa、TLE
因为区间异或和不具有单调性,所以很难找到高效的算法去找到一个区间满足区间异或和不小于 k {k} k且长度最短。区间问题的一个老套路是通过前缀和转化为两个点的问题,而且位运算有点难度的题都会涉及二进制,因为后面需要用到搜索,可以联想到字典树。
原数组转化为前缀和 f {f} f
我们可以枚举区间右端点 r {r} r,然后找到最近的一个下标 l {l} l满足 f [ l ] ⊗ f [ r ] > = k {f[l]\otimes f[r]>=k} f[l]⊗f[r]>=k,那么这个合法区间就是 [ l + 1 , r ] [l+1,r] [l+1,r]
我们可以按 k {k} k的二进制位去找下标 j {j} j:
假设 k {k} k的第 i {i} i位是 s [ i ] {s[i]} s[i], f [ r ] {f[r]} f[r]的第 i {i} i位是 b [ i ] {b[i]} b[i],在字典树中当前遍历到的节点是 r o o t {root} root,一个需要访问的值是 i d {id} id
如果 s [ i ] = = 1 {s[i]==1} s[i]==1,那么 i d = b [ i ] ⊗ 1 {id=b[i] \otimes 1} id=b[i]⊗1,如果此时 t i r [ r o o t ] [ i d ⊗ 1 ] ≠ 0 {tir[root][id\otimes 1]\ne0} tir[root][id⊗1]=0,也就是说这个二进制位可以凑出 1 {1} 1,如果沿着节点 t i r [ r o o t ] [ i d ] = = 0 {tir[root][id]==0} tir[root][id]==0继续搜,那么不管怎么搜都凑得到不小于 k {k} k的数
如果 s [ i ] = = 0 s[i]==0 s[i]==0,那么 i d = b [ i ] {id=b[i]} id=b[i]
如果 t i r [ r o o t ] [ i d ] = = 0 {tir[root][id]==0} tir[root][id]==0,也就是说不管 f [ j ] {f[j]} f[j]取哪一个(第 i + 1 ∼ 30 {i+1 \sim 30} i+1∼30位已经确定),这个二进制位都不能凑出 1 {1} 1,就凑不到不小于 k {k} k的数,就没必要接着搜了
搜索完 f [ r ] {f[r]} f[r]后把 f [ r ] {f[r]} f[r]插入到字典树,同时把遍历的点标记,标记的值为 r {r} r,表示最近一次遍历这个节点的数的下标
比赛过的(3) | 补题(2) |
---|---|
1001 1012 1005 | 1008 1011 |
计算棱长 n-1 正方体中 所有等边三角形, 计算上下底面每一种边长的等边三角形数量,边平行于每个面。
答案 ∑ i = 1 n − 1 8 i 3 \sum^{n-1}_{i=1}8i^3 ∑i=1n−18i3
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MOD = 1e9+7;
int T;
ll n;
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%lld",&n);
n = n % MOD;
ll ans = 2 * n % MOD * n % MOD * (n-1) % MOD * (n-1) % MOD;
printf("%lld\n",ans);
}
return 0;
}
查找字符串中是否含有相应子串
#include
#include
using namespace std;
int T; string s,s0="114514" ;
int main()
{
cin>>T;
for(int t=1;t<=T;t++)
{
cin>>s;
if(s.find(s0)!=string::npos) // npos表示没有找到
cout<<"AAAAAA"<
签到,每次可以将序列中当前字母插入序列之首或序列之尾。问字典序最小可能有几种方式。
答案是 2 序 列 开 头 相 同 字 母 数 − 1 2^{序列开头相同字母数-1} 2序列开头相同字母数−1,对后面字母只能插在特定位置
#include
#include
#define mod 1000000007
using namespace std;
int T,l,maxl; string s; long long ans;
long long pow(int x);
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
cin>>T;
for(int t=1;t<=T;t++)
{
cin>>l>>s; maxl=0;
for(int i=1;i
f [ i ] [ j ] f[i][j] f[i][j]表示第i个科目拿到k分的最短时间,跑一遍01背包。
m x [ i ] [ j ] mx[i][j] mx[i][j]表示第i个科目花费k天的最大分数。
d p [ i ] [ j ] dp[i][j] dp[i][j]表示i天挂k科的最大分数
初始化 f [ i ] [ j ] = i n f , d p [ i ] [ j ] = − i n f , m x [ i ] [ j ] = 0 f[i][j]=inf, dp[i][j] = -inf, mx[i][j] = 0 f[i][j]=inf,dp[i][j]=−inf,mx[i][j]=0
状态转移
f[i][j] = f[i][j-gra]+day
01背包
mx[i][f[i][k]] = k
k = 1~100
dp[j][k] = max(dp[j][k],dp[j-l][k]+mx[i][l]) mx[i][l] >= 60
dp[j][k] = max(dp[j][k],dp[j-l][k-1]+mx[i][l]) mx[i][l] < 60
i = 1~n j = t~1 k = 0~p l = 1 − m i n ( f [ i ] [ 100 ] , j ) l = 1-min(f[i][100],j) l=1−min(f[i][100],j)
#include
#include
#include
#include
#include
#include
#include
#define x first
#define y second
using namespace std;
const int N = 55, S = 105, Time = 510, INF = 0x3f3f3f3f;
int g[N][S], f[N][Time][5];
int n, idx, m, t, p;
typedef pair P;
unordered_map mp;
vector scores[N];
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
mp.clear();
idx = 0;
memset(g, 0x3f, sizeof g);
memset(f, -0x3f, sizeof f);
scanf("%d", &n);
for(int i = 1; i <= n; i++) scores[i].clear();
for(int i = 1; i <= n; i++)
{
string s;
cin >> s;
mp[s] = ++idx;
}
scanf("%d", &m);
for(int i = 1; i <= m; i++)
{
string s;
int score, day;
cin >> s;
scanf("%d%d", &score, &day);
scores[mp[s]].push_back({score, day});
}
scanf("%d%d", &t, &p);
for(int id = 1; id <= n; id++)
{
g[id][0] = 0;
for(int i = 1, sz = scores[id].size(); i <= sz; i++)
{
int score = scores[id][i - 1].x, day = scores[id][i - 1].y;
for(int j = 100; j >= score; j--)
{
g[id][j] = min(g[id][j], g[id][j - score] + day);
}
}
}
f[0][0][0] = 0;
for(int i = 1; i <= n; i++)
{
for(int j = t; j >= 0; j--)
{
for(int k = 0; k <= 100; k++)
{
int day = g[i][k];
for(int pp = p; pp >= 0; pp--)
{
int kk = k < 60 ;
if(pp - kk < 0 || j - day < 0) continue;
f[i][j][pp] = max(f[i][j][pp], f[i - 1][j - day][pp - kk] + k);
}
}
}
}
int ans = -INF;
for(int i = 0; i <= t; i++)
for(int pp = 0; pp <= p; pp++)
{
ans = max(ans, f[n][t][pp]);
}
if(ans <= -INF / 2) ans = -1;
printf("%d\n", ans);
}
}
要求 C k = max ( A i , B j ) C_k = \max{(A_i, B_j)} Ck=max(Ai,Bj) 满足i & j ≥ k \ge k ≥k
求 ∑ C i \sum C_i ∑Ci
思路:
考虑数组D
满足 D k = m a x ( A i × B j ) D_k = m a x ( A_i × B_j ) Dk=max(Ai×Bj) ( i & j = = k )
C j = m a x { D i } , ( i ≥ j ) C_j=max\{D_i\},(i\ge j) Cj=max{Di},(i≥j)
计算这个只需要从小到大遍历
思考 k = = i k==i k==i的时候, j j j为多少
举例
设n-1
的二进制位为10110
,从大到小遍历所有的下标i
。
i
的二进制位10110
时,若使得 i & j = = k ,则j
可能的二进制位为:10110
(j
的取值不能超过下标)。i
的二进制位10101
时,则j
可能的二进制为:10101
。i
的二进制位10100
时,则j
可能的二进制为:10100
、10101
。i
的二进制位10011
时,则j
可能的二进制为:10011
。i
的二进制位10010
时,则j
可能的二进制为:10010
、10011
、10110
。i
的二进制位10001
时,则j
可能的二进制为:10001
、10011
、10101
。i
的二进制位10000
时,则j
可能的二进制为:10000
、10001
、10010
、10100
、10011
、10110
、10110
。!!
当i
为10000
去找j
进行比较时,很明显能发现:
j=10011
、j=10110
、j=10110
在前几个j
时已经比较过了,没必要继续比较。可以发现,如果将i
的二进制位中的某一个0
变成1
得到j
,那么 i & j = = i 。
当然,如果变两位也可以使得 i & j = = i ,但是会重复比较(根据例子可以看出来)。
因为 A i A_i Ai 和 B i B_i Bi 都有可能是负数,所以要存min
和max
。
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int T;
const int mod = 998244353, MAXN = 2e6 + 50;
const ll INF = 1e18; // 这个得开足够大
ll a[MAXN], A[MAXN], b[MAXN], B[MAXN], n;
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%lld", &n);
for(int i = 0;i < n;i++) {
scanf("%lld", &a[i]);
A[i] = a[i];
}
for(int i = 0;i < n;i++) {
scanf("%lld", &b[i]);
B[i] = b[i];
}
int m = 0;
while((1 << m) <= n) m++;
//printf("m = %lld\n", m);
for(int i = n; i < (1 << m); i++)
{
a[i] = b[i] = INF;
A[i] = B[i] = -INF;
}
// i & j == k 时, tmp 与 i 只有一位不同才进行更新
for(int i = (1 << m) - 1; i >= 0; i--)
{
for(int j = 0;j < m;j++)
{
int tmp = 1 << j;
if((tmp & i) == 0)
{
A[i] = max(A[i], A[i ^ tmp]);
a[i] = min(a[i], a[i ^ tmp]);
B[i] = max(B[i], B[i ^ tmp]);
b[i] = min(b[i], b[i ^ tmp]);
}
}
}
// 逆序求i & j >= k
ll last = -INF, res = 0;
for(int i = n - 1; i >= 0; i--)
{
ll ans = -INF;
ans = max(ans, a[i] * b[i]);
ans = max(ans, A[i] * b[i]);
ans = max(ans, A[i] * B[i]);
ans = max(ans, a[i] * B[i]);
ans = max(ans, last);
last = ans;
//printf("ans = %lld\n", ans);
res = (res + ans + mod) % mod;
}
printf("%lld\n", (res % mod + mod) % mod);
}
return 0;
}
比赛过的(2) | 补题(1) |
---|---|
E J | B |
按照每一列找规律
s [ 2 ] = s [ 1 ] 3 s[2] = s[1]^3 s[2]=s[1]3
s [ i + 1 ] = s [ i ] ∗ b a s e 2 − s [ i − 1 ] s[i+1] = s[i] * base ^ 2 - s[i-1] s[i+1]=s[i]∗base2−s[i−1]
#include
using namespace std;
typedef unsigned long long ll;
struct node{
ll i, cnt;
};
int T;
ll n;
ll ans[2000000], id;
void solve()
{
ans[0] = 1;
for(ll i = 2;i <= 1e6;i++)
{
__int128 tmp = i*i*i, lasttmp = i, llasttmp = i;
ans[++id] = tmp;
while(true)
{
lasttmp = tmp;
tmp = tmp * i * i - llasttmp;
if(tmp > 1e18) break;
ans[++id] = tmp;
llasttmp = lasttmp;
}
}
sort(ans,ans+1+id);
}
int main()
{
solve();
scanf("%d",&T);
while(T--)
{
scanf("%llu",&n);
ll res = upper_bound(ans,ans+id+1,n)-ans;
printf("%llu\n",res);
}
return 0;
}
//1000000000000000000
求边颜色相同的三角形
用总数减去边异色的三角形(考虑顶点)
a n s = ∑ w [ i ] ∗ ( n − 1 − w [ i ] ) ans = \sum w[i] * (n-1-w[i]) ans=∑w[i]∗(n−1−w[i])
C n 3 − a n s / 2 C_n^3 - ans / 2 Cn3−ans/2
#include
#include
using namespace std;
unsigned z1,z2,z3,z4,b,u;
bitset<8010> edge[8010];
unsigned get()
{
b=((z1<<6)^z1)>>13;
z1=((z1&4294967294U)<<18)^b;
b=((z2<<2)^z2)>>27;
z2=((z2&4294967288U)<<2)^b;
b=((z3<<13)^z3)>>21;
z3=((z3&4294967280U)<<7)^b;
b=((z4<<3)^z4)>>12;
z4=((z4&4294967168U)<<13)^b;
return (z1^z2^z3^z4);
}
bool read()
{
while(!u) u=get();
bool res=u&1;
u>>=1; return res;
}
void srand(int x)
{
z1=x;
z2=(~x)^0x233333333U;
z3=x^0x1234598766U;
z4=(~x)+51;
u = 0;
}
int w[8010]={0}; long long ans=0;
int main()
{
int n,seed;
scanf("%d%d",&n,&seed);
srand(seed);
for(int i=0;i
题意:2*2的正方形染3个第四个格子自动染色,染每个格子都有本身的花费,求总共最小的花费。
所有格子被染成黑色的充要条件为:
将每一行和每一列作为一个顶点,对应格子的权重为当前行到当前列的连边,求其最小生成树。
原因为:每个各自表示每一行和每一列,需要最少填满n+m个格子才能全部染黑。
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int n, m;
int a, b, c, d, p;
int A[25000001], id;
struct edge{
int u, v;
int w;
bool operator<(const edge &a)
{
return w < a.w;
}
}E[25000001];
int fa[10001];
int find(int x)
{
if(x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
int main()
{
scanf("%d%d%d%d%d%d%d",&n,&m,&a,&b,&c,&d,&p);
for(int i = 1;i <= n+m;i++) fa[i] = i;
A[0] = a;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
int &x = A[m *(i - 1) + j - 1];
A[m *(i - 1) + j] = ((ll)x * x * b + x * c + d) % p;
}
}
/*
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
printf("%d ",A[m*(i-1)+j]);
}
printf("\n");
}
*/
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
E[id++] = {i,j+n,A[m*(i-1)+j]};
}
}
sort(E,E+id);
ll ans = 0, cnt = 0;
for(int i = 0;i < id;i++)
{
//printf("%d %d %d\n",E[i].u,E[i].v,E[i].w);
if(cnt >= n+m-1) break;
int u = find(E[i].u), v = find(E[i].v);
if(u != v)
{
ans += E[i].w;
fa[u] = v;
cnt++;
}
}
printf("%lld\n",ans);
return 0;
}
给定$ n(1\leq n\leq 4) 张 牌 , 每 张 牌 的 点 数 从 1 到 13 , 在 类 似 24 点 的 规 则 下 , 求 能 否 构 造 出 包 含 n 个 变 量 的 表 达 式 , 其 值 为 张牌,每张牌的点数从1 到13,在类似24点的规则下,求能否构造出包含n个变量的表达式,其值为 张牌,每张牌的点数从1到13,在类似24点的规则下,求能否构造出包含n个变量的表达式,其值为m(1\leq m\leq 10^9)$。与24点规则不同,计算过程中必须出现分数,且最后结果为整数。
比赛过的(4) | 补题(0) |
---|---|
F I C J |
每次可以删掉一条边或者一个子树
运用并查集,先将图删成一棵树再一次去掉子树
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 110;
int n, m;
int fa[MAXN], sz[MAXN];
int find(int x)
{
if(x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++) fa[i] = i;
int cnt = n;
while(m--)
{
int u, v;
scanf("%d%d",&u,&v);
int x = find(u), y = find(v);
if(x != y)
{
fa[x] = y;
cnt--;
}
else cnt++;
}
if(cnt % 2 == 0) puts("Bob");
else puts("Alice");
return 0;
}
答案为逆序对数 - 不相邻的从1枚举到n的逆序对
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 2e5+50;
int n, m;
int a[MAXN], c[MAXN], b[MAXN];
ll w[MAXN];
struct node{
int num, id;
bool operator<(const node &a)
{
return num < a.num;
}
}N[MAXN];
inline int lowbit(int x)
{
return x & (-x);
}
void change(int i, int k)
{
while(i <= n)
{
c[i] += k;
i += lowbit(i);
}
}
int sum(int x)
{
int ret = 0;
while(x)
{
ret += c[x];
x -= lowbit(x);
}
return ret;
}
int main()
{
scanf("%d",&n);
// 以下求区间逆序对O(nlogn)
for(int i = 1;i <= n;i++) {
scanf("%d",&a[i]);
N[i].id = i;
N[i].num = a[i];
}
sort(N+1,N+n+1);
for(int i = 1;i <= n;i++) b[N[i].id] = i;
ll ans = 0;
for(int i = n;i > 0;i--)
{
change(b[i],1);
ans += sum(b[i] - 1);
}
ll res = 0;
for(int i = 2;i <= n;)
{
if(N[i].id < N[i-1].id)
{
w[i] = 1;
i++;
}
i++;
}
for(int i = 1;i <= n;i++) res += w[i];
printf("%lld\n",ans-res);
return 0;
}
构造最长公共子序列为特定值的情况
注意:序列长度可以无限大,且一个完整的子序列只要有三个字母
#include
#include
#include
#include
using namespace std;
const int MAXN = 1010;
int a, b, c, n;
char s1[MAXN], s2[MAXN], s3[MAXN], id;
int main()
{
scanf("%d%d%d%d",&a,&b,&c,&n);
int minn = min(c,min(a,b));
int i = 0;
for(;i < minn;i++)
{
s1[i] = 'a';
s2[i] = 'a';
s3[i] = 'a';
}
if(a == minn)
{
if(b+c-a > n) {
puts("NO");
return 0;
}
for(;i < b;i++)
{
s1[i] = 'c';
s2[i] = 'b';
s3[i] = 'b';
}
for(;i < b+c-a;i++)
{
s1[i] = 'c';
s2[i] = 'b';
s3[i] = 'c';
}
for(;i < n;i++)
{
s1[i] = 'c';
s2[i] = 'b';
s3[i] = 'd';
}
}
else if(b == minn)
{
if(c+a-b > n){
puts("NO");
return 0;
}
for(;i < a;i++)
{
s1[i] = 'b';
s2[i] = 'b';
s3[i] = 'c';
}
for(;i < c+a-b;i++)
{
s1[i] = 'c';
s2[i] = 'b';
s3[i] = 'c';
}
int id = 0;
for(;i < n;i++)
{
s1[i] = 'd';
s2[i] = 'b';
s3[i] = 'c';
}
}
else{
if(a+b-c > n) {
puts("NO");
return 0;
}
for(;i < a;i++)
{
s1[i] = 'b';
s2[i] = 'b';
s3[i] = 'c';
}
for(;i < a+b-c;i++)
{
s1[i] = 'b';
s2[i] = 'c';
s3[i] = 'c';
}
for(;i < n;i++)
{
s1[i] = 'b';
s2[i] = 'd';
s3[i] = 'c';
}
}
printf("%s\n",s1);
printf("%s\n",s2);
printf("%s\n",s3);
return 0;
}
求两个数组子区间最大平均值之和
二分查找子区间平均值最大值
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 1e5+50;
int n, m, x, y;
double a[MAXN], b[MAXN], tmp[MAXN], sa[MAXN], sb[MAXN];
// 二分查找子区间平均值最大值(二分mid,然后前缀和数组进行处理)
bool check(double a[], int len, int limit, double x, double sa[])
{
for(int i = 1;i <= len;i++) tmp[i] = a[i] - x;
sa[0] = 0;
for(int i = 1;i <= len;i++) sa[i] = sa[i-1] + tmp[i];
double ans = -1e6, res = 1e6;
for(int i = limit;i <= len;i++)
{
res = min(res, sa[i - limit]);
ans = max(ans, sa[i] - res);
}
if(ans >= 0) return true;
else return false;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&x,&y);
for(int i = 1;i <= n;i++) scanf("%lf",&a[i]);
for(int i = 1;i <= m;i++) scanf("%lf",&b[i]);
double r = 1e9, l = 0;
double ans = 0;
while(r - l > 1e-10)
{
double mid = (l+r) / 2;
if(check(a, n, x, mid, sa)) l = mid;
else r = mid;
}
ans += r;
r = 1e9, l = 0;
while(r - l > 1e-10)
{
double mid = (l+r) / 2;
if(check(b, m, y, mid, sb)) l = mid;
else r = mid;
}
ans += r;
printf("%.10f\n",ans);
return 0;
}
比赛过的(2) | 补题(1) |
---|---|
1011 1007 | 1004 |
线段树中区间长度最多为k,问n个结点最多有几个区间
深搜,用unordered_map存n个结点的区间个数
偶数 mp[k] = 2*dfs(k / 2) + 1
奇数 mp[k] = dfs(k / 2) + dfs(k / 2 + 1) + 1
深搜+记忆化搜索
#include
#include
#include
#include
#include
using namespace std;
typedef unsigned long long ll;
const int MAXN = 1e7+50;
int T;
ll k, n;
unordered_mapmp;
void init(ll n)
{
mp.clear();
ll base = 2;
for(ll i = n;i <= k;i *= 2)
{
mp[i] = base-1;
base *= 2;
}
}
ll dfs(ll k, ll n)
{
if(k <= n) return mp[n];
if(mp[k]) return mp[k];
if(k % 2 == 0) {
mp[k] = 2*dfs(k / 2, n) + 1;
return 2*dfs(k / 2, n) + 1;
}
else {
mp[k] = dfs(k / 2, n) + dfs(k / 2 + 1, n) + 1;
return dfs(k / 2, n) + dfs(k / 2 + 1, n) + 1;
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%llu%llu",&k,&n);
init(n);
ll cnt = dfs(k, n);
printf("%llu\n",cnt);
}
return 0;
}
简单前缀和问题(只有区间查询)
存储每个数前最近的1,维护区间和
r[rr] - r[max(fa[rr], ll)-1]
#include
#include
#include
#include
using namespace std;
const int MAXN = 1e6+50;
int T, n, q;
struct color{
int op, r0, g0, b0;
}Color[MAXN];
int fa[MAXN];
int r[MAXN], g[MAXN], b[MAXN];
void merge(int x)
{
if(Color[x].op == 1) fa[x] = x;
else {
fa[x] = fa[x-1];
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&q);
for(int i = 1;i <= n;i++) fa[i] = i;
for(int i = 1;i <= n;i++)
{
int op, x;
scanf("%d%X",&op,&x);
Color[i].op = op;
Color[i].b0 = x % 256;
Color[i].r0 = x / 256 / 256;
Color[i].g0 = x / 256 % 256;
merge(i);
r[i] = r[i-1] + Color[i].r0;
b[i] = b[i-1] + Color[i].b0;
g[i] = g[i-1] + Color[i].g0;
}
while(q--)
{
int ll, rr;
scanf("%d%d",&ll,&rr);
int ans1 = min(255,r[rr] - r[max(fa[rr], ll)-1]);
int ans2 = min(255,g[rr] - g[max(fa[rr], ll)-1]);
int ans3 = min(255,b[rr] - b[max(fa[rr], ll)-1]);
printf("%02X%02X%02X\n",ans1,ans2,ans3);
}
}
return 0;
}
题意:平面上有n条直线,Alice会进行n次操作,每次选出k条直线(k=1,2,3,…n),Bob将画一条直线,若与选中的直线有交点则惩罚加一。Alice想让惩罚最大,Bob反之。最后输出每次操作的惩罚值。
两条直线存在公共点当且仅当它们重合或者它们斜率不同,因此Bob的最优策略一定是避开斜率出现次数最多的那些直线。Alice 为了让 Bob 与尽量多的直线相交,最优策略就是最小化斜率出现次数的最大值,所以不断从每种斜率的直线中各选一种即可。
注意:斜率不好直接储存,因为做了除法,可能会出现精度丢失或这除以0的情况。这里可以用pair储存约分后的最简坐标。不需要直接存相同斜率直线的个数,用j来表示Alice取出直线的阶段,f[j]表示j阶段有多少斜率不同的直线。
#include
#include
#include
#include
using namespace std;
typedef pair PII;
const int MAXN = 1e5 + 50;
int T, n;
PII p[MAXN];
int f[MAXN];
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a % b);
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(int i = 1;i <= n;i++)
{
int x1, x2, y1, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
int dx = x1 - x2, dy = y1 - y2;
if(dx == 0) dy = 1;
else if(dy == 0) dx = 1;
else {
if(dx < 0) {dx = -dx, dy = -dy;}
int d = gcd(abs(dx), abs(dy));
dx /= d, dy /= d;
}
p[i].first = dx, p[i].second = dy;
}
sort(p + 1, p + n + 1);
// for(int i = 0;i < n;i++) printf("%d %d\n", p[i].first, p[i].second);
int i, j;
for(i = 1; i <= n; i++) f[i] = 0;
for(i = 1; i <= n; i = j)
{
for(j = i; p[i] == p[j] && j <= n; j++);
for(int k = 1; k <= j - i; k++) f[k]++;
}
for(i = j = 1; i <= n; i++)
{
while(f[j] == 0) j++;
f[j]--;
printf("%d\n", i - j);
}
}
return 0;
}
比赛过的(2) | 补题(1) |
---|---|
1001 1009 | 1002 |
判断+后面是不是0 && 开头必须是0
#include
#include
#include
#include
#include
using namespace std;
string s;
int T;
int main()
{
scanf("%d",&T);
while(T--)
{
bool flag = true;
cin >> s;
if(s[0] != '0') {
flag = false;
}
for(int i = 0;i < s.size();i++)
{
if(s[i] == '+') {
if(s[i+1] != '0') {
flag = false;
break;
}
}
}
if(flag) puts("YES");
else puts("NO");
}
}
输入字符 倒序判断(汉字可能不连通)
#include
#include
#include
#include
using namespace std;
int T; char c=1; bool in[110];
struct node{
int l, r;
bool operator<(const node &a)
{
return l < a.l;
}
}N[10];
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
scanf("%d",&T);
for(int t=1;t<=T;t++)
{
for(int i=1;i<=100;i++)
in[i]=0;
for(int i=1;i<=30;i++)
{
for(int j=1;j<=100;j++)
{
c=getchar();
while(c!='.'&&c!='#') c=getchar();
if(c=='#') in[j]=1;
}
}
//for(int i = 1;i <= 100;i++) printf("%d%c",in[i],i % 10 == 0 ? '\n' : ' ');
printf("Case #%d:\n",t);
int cnt = 0, i;
for(i=100;i>=1 && cnt < 6;i--)
{
if(in[i]==0)
continue;
N[cnt].r = i;
while(in[i]&&i>=1) i--;
N[cnt].l = i+1;
cnt++;
}
while(in[i] == 0) i--;
N[cnt].r = i;
int j = 1;
while(in[j] == 0) j++;
N[cnt].l = j;
cnt++;
sort(N,N+cnt);
for(int i = 0;i < cnt;i++)
{
printf("%d %d\n",N[i].l, N[i].r);
}
}
return 0;
}
dfs暴搜,以每个节点为根进行dfs,然后求出 w [ i ] [ j ] w[i][j] w[i][j]
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 2030;
const int DA = 19560929;
const int mod1 = 1e9+7;
const int mod2 = 1e9+9;
int T, n, c[MAXN], w[MAXN][MAXN], head[MAXN], id, cnt[MAXN];
bool st[MAXN];
ll po1[MAXN], po2[MAXN];
void init()
{
po1[0] = 1, po2[0] = 1;
for(int i = 1;i <= 2010;i++)
{
po1[i] = po1[i-1] * DA % mod1;
po2[i] = po2[i-1] * DA % mod2;
}
}
struct edge{
int next, to;
}E[MAXN * 4];
inline void addedge(int u, int v)
{
E[id].to = v;
E[id].next = head[u];
head[u] = id++;
return;
}
void dfs(int u, int cur)
{
st[cur] = true;
for(int i = head[cur];i != -1;i = E[i].next)
{
int p = E[i].to;
if(!st[p])
{
if(cnt[c[p]]) w[u][p] = w[u][cur];
else w[u][p] = w[u][cur] + 1;
cnt[c[p]]++;
dfs(u, p);
cnt[c[p]]--;
}
}
}
int main()
{
init();
scanf("%d",&T);
while(T--)
{
memset(head,-1,sizeof(head));
memset(w,0,sizeof(w));
id = 0;
scanf("%d",&n);
for(int i = 2;i <= n;i++)
{
int x;
scanf("%d",&x);
addedge(i, x);
addedge(x, i);
}
for(int i = 1;i <= n;i++) scanf("%d",&c[i]);
for(int i = 1;i <= n;i++)
{
w[i][i] = 1;
for(int j = 1;j <= n;j++) st[j] = false;
for(int j = 1;j <= n;j++) cnt[j] = 0;
cnt[c[i]] = 1;
dfs(i,i);
}
/*
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= n;j++)
{
printf("%d ",w[i][j]);
}
printf("\n");
}
*/
for(int i = 1;i <= n;i++)
{
ll res1 = 0, res2 = 0;
for(int j = 1;j <= n;j++)
{
res1 = (res1 + w[i][j] * po1[j-1] % mod1) % mod1;
res2 = (res2 + w[i][j] * po2[j-1] % mod2) % mod2;
}
printf("%lld %lld\n",res1,res2);
}
}
return 0;
}
比赛过的(3) | 补题(1) |
---|---|
H K B | D |
构造横竖斜着的连续三个矩阵中的数不相等
如下构造
1 1 0 0 1 1 0 0
0 0 1 1 0 0 1 1
1 1 0 0 1 1 0 0
0 0 1 1 0 0 1 1
#include
using namespace std;
int n,m;
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(j%4==1||j%4==2)
printf("%d",i%2);
else
printf("%d",1-i%2);
}
printf("\n");
}
return 0;
}
题意:求满足:区间最大值-最小值>k的不同区间个数
枚举每一个左端点, 寻找最小右端点
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 50;
int n, m;
int a[MAXN], q1[MAXN], q2[MAXN];
int main()
{
scanf("%d%d",&n,&m);
for(int i = 0;i < n;i++) scanf("%d",&a[i]);
while(m--)
{
int k;
scanf("%d",&k);
ll ans = 0;
int hh1 = 0, hh2 = 0, tt1 = -1, tt2 = -1;
q1[++tt1] = 0, q2[++tt2] = 0;
for(int i = 0, j = 0;i < n;i++)
{
while(hh1 <= tt1 && q1[hh1] < i) hh1++;
while(hh2 <= tt2 && q2[hh2] < i) hh2++;
while(j < n && a[q1[hh1]] - a[q2[hh2]] <= k)
{
j++;
while(hh1 <= tt1 && a[q1[tt1]] < a[j]) tt1--;
while(hh2 <= tt2 && a[q2[tt2]] > a[j]) tt2--;
q1[++tt1] = j; q2[++tt2] = j;
}
if(j < n) ans += n-j;
else break;
}
printf("%lld\n",ans);
}
return 0;
}
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 1e5+5, M = 22;
int n, m;
ll k;
int a[MAXN], maxx[MAXN][M], minn[MAXN][M], Log[MAXN];
void init()
{
for(int j = 0;j < M;j++)
{
for(int i = 1;i + (1 << j) - 1 <= n;i++)
{
if(j == 0) {
maxx[i][j] = a[i];
minn[i][j] = a[i];
}
else {
maxx[i][j] = max(maxx[i][j - 1], maxx[i + (1 << (j - 1))][j - 1]);
minn[i][j] = min(minn[i][j - 1], minn[i + (1 << (j - 1))][j - 1]);
}
}
}
}
void cal() // 求Log 否则cmath库中 log运行时间长
{
Log[1] = 0;
for(int i = 2;i <= MAXN;i++) Log[i] = Log[i/2]+1;
}
bool check(int l, int r, ll k)
{
int lo = Log[r-l+1];
int maxn = max(maxx[l][lo], maxx[r - (1 << lo) + 1][lo]);
int minx = min(minn[l][lo], minn[r - (1 << lo) + 1][lo]);
if(maxn - minx > k) return true;
else return false;
}
int main()
{
cal();
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
init();
while(m--)
{
scanf("%lld",&k);
ll ans = 0; // 注意每次初始化
for(int i = 1, j = 1;i <= n;i++)
{
while(!check(i, j, k) && j <= n && i <= j) j++;
if(j <= n) ans = ans+n-j+1;
}
printf("%lld\n", ans);
}
return 0;
}
题意:开每一个箱子的代价为 w i w_i wi,询问一次剩下有几个黑球代价为c,每个盒子都有0.5的概率白 or 黑,且相互独立,问数学期望最小为多少。
不再开箱的条件为剩下球的颜色相同。
r e s = ∑ i = 1 n ( 1 2 ) n − i ( c + ∑ w i ) res = \sum_{i = 1}^{n}(\frac{1}{2})^{n-i}(c+\sum w_i) res=∑i=1n(21)n−i(c+∑wi)
#include
#include
#include
#include
using namespace std;
const int M = 1e5+50;
double w[M], sum[M];
double c;
double poww[M];
int n;
void init()
{
poww[0] = 1;
for(int i = 1;i <= 1e5+5;i++) poww[i] = poww[i-1] * 0.5;
return;
}
bool cmp(double a, double b)
{
return a > b;
}
int main()
{
init();
scanf("%d%lf",&n,&c);
for(int i = 1;i <= n;i++) scanf("%lf",&w[i]);
sort(w+1,w+1+n);
sum[0] += c;
for(int i = 1;i <= n;i++) sum[i] = sum[i-1] + w[i];
double res = 0, las = 1;
res += sum[0] * poww[n-1];
for(int i = 1;i <= n-1;i++)
{
res += sum[i] * poww[n-i];
// printf("%.10f\n",res);
}
printf("%.10f\n",min(res, sum[n]-c));
return 0;
}
计数时: d p [ i ] [ j ] dp[i][j] dp[i][j]表示 a [ 1 − i ] , b [ 1 − j ] a[1-i],b[1-j] a[1−i],b[1−j]的公共子序列个数(包含空串)
dp[i][j] = dp[i][j-1]+dp[i-1][j]; if(a[i] == b[j])
dp[i][j] = dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1] if(a[i] != b[j]) 不加1是因为本身dp计数就有空串
后面根据组合数,后 n − i n-i n−i和 m − j m-j m−j个中任取 i i i个
KaTeX parse error: Undefined control sequence: \C at position 26: …^{min(n-i,m-j)}\̲C̲_{n-i}^{k}\C^{k…
对于大数的处理,先定义大数的add, sub, mul避免出错
预处理逆元
int qmi(int a, int b, int mod)
{
int res = 1;
while(b)
{
if(b & 1) res = (ll)res * a % mod;
b >>= 1;
a = (ll)a * a % mod;
}
return res;
}
void init()
{
fact[0] = 1, infact[0] = 1;
for(int i = 1;i <= 2*5005;i++) {
fact[i] = mul(fact[i-1], i);
infact[i] = mul(infact[i-1], qmi(i, MOD-2, MOD));
}
return;
}
ll Cal(int n, int m)
{
if(n == 0 && m == 0) return 1;
return mul(mul(fact[n], infact[m]), infact[n-m]);
}
ll exgcd(ll a, ll b , ll &x , ll &y){
if(b){
int r = exgcd(b , a % b , y , x);
y -= x * (a / b);
return r;
}
else{
x = 1 , y = 0;
return a;
}
}
ll inv(ll x){
ll a , b , c, d;
a = x , b = mod;
exgcd(a,b,c,d);
c %= mod;
return c < 0 ? mod + c : c;
}
ll dp[N][N];
ll F[2 * N];
ll invF[2 * N];
void pre(){
F[0] = 1;
for(int i=1; i < 2 * N ; i++){
F[i] = Mul(F[i-1] , i);
}
for(int i = 0; i < 2 * N; i++){
invF[i] = inv(F[i]);
}
}
ll C(ll n , ll x){
if(n == 0 && x == 0)return 1;
return F[n] * invF[x] % mod * invF[n - x] % mod;
}
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int M = 5050;
char a[M], b[M];
ll fact[2*M], infact[2*M];
ll dp[M][M];
const int MOD = 1e9+7;
ll add(ll a, ll b)
{
return a + b >= MOD ? a + b - MOD : a + b;
}
ll sub(ll a, ll b)
{
return a - b >= 0 ? a - b : a - b + MOD;
}
ll mul(ll a, ll b)
{
return a * b % MOD;
}
int qmi(int a, int b, int mod)
{
int res = 1;
while(b)
{
if(b & 1) res = (ll)res * a % mod;
b >>= 1;
a = (ll)a * a % mod;
}
return res;
}
void init()
{
fact[0] = 1, infact[0] = 1;
for(int i = 1;i <= 2*5005;i++) {
fact[i] = mul(fact[i-1], i);
infact[i] = mul(infact[i-1], qmi(i, MOD-2, MOD));
}
return;
}
ll Cal(int n, int m)
{
if(n == 0 && m == 0) return 1;
return mul(mul(fact[n], infact[m]), infact[n-m]);
}
int main()
{
init();
cin >> a+1 >> b+1;
int n = strlen(a+1), m = strlen(b+1);
dp[0][0] = 1;
for(int i = 1;i <= n;i++) dp[i][0] = 1;
for(int i = 1;i <= m;i++) dp[0][i] = 1;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
dp[i][j] = sub(add(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]);
if(a[i] == b[j]) dp[i][j] = add(dp[i][j], dp[i-1][j-1]);
}
}
/*
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++) printf("%d ",dp[i][j]);
printf("\n");
}
*/
ll ans = 0, res = 0;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j <= m;j++)
{
int x = n-i, y = m-j;
if(a[i] < b[j]) {
res = mul(dp[i-1][j-1], Cal(x+y, x));
ans = add(ans, res);
}
}
}
printf("%lld\n", ans);
return 0;
}
比赛过的(2) | 补题(0) |
---|---|
I F |
题意:找区间的交为给定区间的并
每个区间必须包括所有区间,所以取所有区间左端点和离它最远区间右端点
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 1010;
int T, n, m;
int cf[MAXN * 2], cnt[MAXN];
struct node{
int l, r;
bool operator<(node &a)
{
return l < a.l;
}
}N[MAXN];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
memset(cf, 0, sizeof(cf));
memset(cnt, 0, sizeof(cnt));
for(int i = 0;i < m;i++)
{
int l, r;
scanf("%d%d",&l, &r);
if(l <= r) {
cf[r+1]--;
cf[l]++;
}
else {
cf[r+n+1]--;
cf[l]++;
}
}
for(int i = 1;i <= 2 * n;i++)
{
cnt[i] = cnt[i-1] + cf[i];
}
for(int i = 1;i <= n;i++) cnt[i] += cnt[i+n];
/*
for(int i = 1;i <= n;i++) printf("%d ", cnt[i]);
printf("\n");
*/
int id = 0;
for(int i = 1;i <= n;)
{
while(!cnt[i]) i++;
if(i > n) break;
N[id].l = i;
while(cnt[i]) i++;
N[id].r = min(n, i-1);
id++;
}
/*
printf("\n");
for(int i = 0;i < id;i++) printf("%d %d\n", N[i].l, N[i].r);
printf("\n");
*/
sort(N, N+id);
printf("%d\n", id);
printf("%d %d\n", N[0].l, N[id-1].r);
for(int i = 1;i < id;i++)
{
printf("%d %d\n", N[i].l, N[i-1].r);
}
}
return 0;
}
/*
2
7 3
2 4
6 7
7 1
7 2
5 1
7 2
*/
答案为 m a x ( m a x ( a i ) , 平 均 数 ) max(max(a_i), 平均数) max(max(ai),平均数)
每次直接模拟,超过时间换一个锅
#include
using namespace std;
int n,m,num;
long long cnt,a[100010],sum,ans=0,maxx;
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum+=a[i];
if(a[i]>maxx) maxx=a[i];
}
ans=sum/m;
if(ans*m
比赛过的(3) | 补题(0) |
---|---|
1003 1006 1007 |
#include
#include
#include
#include
using namespace std;
typedef long long ll;
int T;
int main()
{
scanf("%d",&T);
while(T--)
{
ll n, k;
scanf("%lld%lld", &n, &k);
if(n > k + 1) puts("No");
else puts("Yes");
}
return 0;
}
/*
5
1000000000000000000 999999999999999999
*/
#include
#include
#include
#include
#include
using namespace std;
const int MAXN = 2e5+50;
typedef long long ll;
int n, T, a[MAXN];
unordered_mapmp;
ll dfs(int n)
{
if(mp.count(n)) return mp[n];
//if(n == 1) return 1;
//if(n == 2) return 2;
if(n % 3 == 0) {
mp[n] = 3 * dfs(n / 3) + 1;
return mp[n];
}
else if(n % 3 == 1) {
mp[n] = 2 * dfs(n / 3) + dfs(n / 3 + 1) + 1;
return mp[n];
}
else {
mp[n] = dfs(n / 3) + 2 * dfs(n / 3 + 1) + 1;
return mp[n];
}
}
int main()
{
scanf("%d", &T);
while(T--)
{
mp.clear();
mp[1] = 1, mp[2] = 3;
scanf("%d", &n);
for(int i = 1;i <= n;i++) scanf("%d", &a[i]);
ll ans = dfs(n);
printf("%lld\n", ans);
}
return 0;
}
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
ll qpow(ll a, ll b)
{
ll res = 1 % mod;
while(b) {
if(b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
}
ll inv(ll x)
{
return qpow(x, mod - 2);
}
ll pfh(ll n)
{
long long tmp = n * ((n + 1) % mod) % mod * inv(2) % mod;
return tmp;
}
ll lfh(ll n)
{
long long tmp = n * (n + 1) % mod * (2 * n % mod + 1) % mod * inv(6) % mod;
return tmp;
}
int main()
{
int T;
scanf("%d", &T);
while(T --) {
ll n;
scanf("%lld", &n);
n = n % mod;
ll maxn = (pfh(n) * lfh(n)) % mod * n % mod * n % mod;
ll min1 = pfh(n) * lfh(n) % mod;
ll res1 = (((pfh(n) + lfh(n)) % mod - 2 + mod) % mod) * ((n + 2) * (n - 1 + mod) % mod * inv(2) % mod) % mod;
min1 = (min1 + res1) % mod;
printf("%lld\n", min1);
printf("%lld\n", maxn);
}
return 0;
}
/*
10
345345645635
345345654634
567354756
7857645778
57674556
34546567
34546567
3456547
345675678
34566567
23434556
*/
比赛过的(1) | 补题(1) |
---|---|
1001 | 1005 |
#include
#include
#include
#include
using namespace std;
const int MAXN = 4e7+50;
int T, n;
int primes[MAXN], cnt;
bool vis[MAXN];
void get_primes(int n)
{
vis[1] = true;
for(int i = 2;i <= n;i++)
{
if(!vis[i]) primes[cnt++] = i;
for(int j = 0;i * primes[j] <= n;j++)
{
vis[i * primes[j]] = true;
if(i % primes[j] == 0) break;
}
}
}
int main()
{
scanf("%d", &T);
get_primes(MAXN - 2);
while(T--)
{
scanf("%d", &n);
if(n < 0) {
int pos = upper_bound(primes, primes+cnt, -n) - primes;
int ans = primes[pos];
//printf("ans = %d\n", ans);
int res = -n;
while(res <= ans)
{
if(!vis[res+1]) {
printf("%d\n", res * 2 + 2);
break;
}
else if(!vis[2*res+3]) {
printf("%d\n", res * 2 + 3);
break;
}
res++;
}
}
else if(n == 0) printf("3\n");
else{
if(!vis[n]) printf("1\n");
else if(!vis[2 * n + 1] || !vis[2 * n - 1]) printf("2\n");
else {
int pos = upper_bound(primes, primes+cnt, n) - primes;
int ans = primes[pos];
//printf("ans = %d\n", ans);
int res = n;
while(res <= ans)
{
if(!vis[res+1])
{
printf("%d\n", res * 2 + 2);
break;
}
else if(!vis[2 * res + 3])
{
printf("%d\n", res * 2 + 3);
break;
}
res++;
}
}
}
}
return 0;
}
显然 b 1 , b 2 , … , b m b_1,b_2,\dots,b_m b1,b2,…,bm这 m m m个数放在 m m m个不同的集合中,剩下的 n − m n-m n−m个数要放到 m m m个集合中位数。
假设 n = 6 , m = 2 , b 1 = 3 , b 2 = 5 n=6,m=2,b_1=3,b_2=5 n=6,m=2,b1=3,b2=5,那么 1 , 2 , … , n 1,2,\dots,n 1,2,…,n这些数会被 b b b分成 1 , 2 , 4 , 6 1,2,4,6 1,2,4,6这三段,且任意两段的任意一对可以配对消掉。所以最后剩下的数字一定是在一段之内的。
#include
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
int n, m;
cin >> n >> m;
vector f(n + 2);
f[n + 1] = 1;
while (m--) {
int v;
cin >> v;
f[v] = 1;
}
vector> size;
int have = 0;
int count = 0;
for (int i = 1; i <= n + 1; i++) {
if (f[i]) {
if (have) {
size.push_back(make_pair(have, count));
}
have = 0;
count += 1;
} else {
have += 1;
}
}
sort(size.begin(), size.end());
if (size.empty()) {
cout << "YES\n";
continue;
}
int sum = 0;
for (auto s: size) {
sum += s.first;
}
if (size.back().first <= sum - size.back().first + size.back().second) {
cout << "YES\n";
} else {
cout << "NO\n";
}
}
return 0;
}
未参加
比赛过的(4) | 补题(0) |
---|---|
E D A K |
#include
#include
#include
using namespace std;
bool is_prime(int x)
{
for(int i = 2; i <= x / i; i ++) {
if(x % i == 0) {
return false;
}
}
return true;
}
int main()
{
int T;
scanf("%d", &T);
while(T --) {
int x;
scanf("%d", &x);
if(((x % 4 == 0 && x % 100 != 0) || (x % 400 == 0)) && is_prime(x)) puts("yes");
else puts("no");
}
return 0;
}
a + b = a & b + a | b
#include
#include
#include
#include
using namespace std;
const int N = 100010;
int n;
int b[N];
int c[N];
int d[N];
bool check(int x1, int y1, int x2, int y2)
{
if(!x1 && !y1 && !x2 && !y2) return true;
if(x1 == 1 && !y1 && !x2 && !y2) return true;
if(x1 == 1 && !y1 && x2 == 1 && !y2) return true;
if(x1 == 1 && y1 == 1 && x2 == 1 && !y2) return true;
if(!x1 && !y1 && x2 == 1 && !y2) return true;
if(x1 == 1 && !y1 && x2 == 1 && y2 == 1) return true;
if(x1 == 1 && y1 == 1 && x2 == 1 && y2 == 1) return true;
return false;
}
int main()
{
scanf("%d", &n);
for(int i = 2; i <= n; i ++) scanf("%d", &b[i]);
for(int i = 2; i <= n; i ++) scanf("%d", &c[i]);
for(int i = 2; i <= n; i ++) d[i] = c[i] - b[i];
for(int i = 2; i <= n; i ++) {
if(d[i] < 0) {
printf("0\n");
return 0;
}
}
for(int i = 0; i < 30; i ++) {
for(int j = 2; j < n; j ++) {
if(!check(b[j] >> i & 1, d[j] >> i & 1, b[j + 1] >> i & 1, d[j + 1] >> i & 1)) {
printf("0\n");
return 0;
}
}
}
int cnt = 1;
for(int i = 0; i < 30; i ++) {
bool flag = true;
for(int j = 2; j <= n; j ++) {
if((b[j] >> i & 1) != 1 || (d[j] >> i & 1) != 0) {
flag = false;
break;
}
}
if(flag) cnt *= 2;
}
printf("%d\n", cnt);
return 0;
}
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int mod = 4933, N = 100010;
ll n, m, k, a, l;
ll qmi(ll a, ll b, ll k)
{
ll res = 1;
while(b) {
if(b & 1) res = res * a % k;
a = a * a % k;
b >>= 1;
}
return res;
}
ll rev(ll x, ll k)
{
return qmi(x, k - 2, k);
}
int main()
{
scanf("%lld%lld%lld%lld%lld", &n, &m, &k, &a, &l);
ll res = a % mod;
ll tmp = 1;
for(int i = 1; i <= k; i ++) {
ll x, y, z;
scanf("%lld%lld%lld", &x, &y, &z);
if(!x) continue;
if(y == 0) {
//printf("%lld\n", a);
//tmp = 0;
continue;
}
y = z - y;
if(y == 0) {
//printf("%lld\n", a);
tmp = 0;
continue;
}
tmp = (ll)(tmp * (ll)rev(z, mod) % mod * (ll)(y % mod)) % mod;
}
printf("%lld\n", (res + tmp) % mod);
return 0;
}
#include
#include
#include
#include
#include
using namespace std;
double pi = acos(-1);
double d, w;
int main()
{
int T;
scanf("%d", &T);
while(T--) {
scanf("%lf%lf", &d, &w);
double x1 = min(d, w);
double x2 = sqrt(w * w + d * d);
int ans = 0;
for(int i = 0; i < 5; i ++) {
int j = floor((pi - x1 * i) / x2);
if(j < 0) break;
ans = max(ans, 2 * i + 3 * j);
}
for(int j = 0; j < 5; j ++) {
int i = floor((pi - x2 * j) / x1);
if(i < 0) break;
ans = max(ans, 2 * i + 3 * j);
}
printf("%d\n", ans + 4);
}
return 0;
}
比赛过的(3) | 补题(3) |
---|---|
1003 1010 1012 | 1007 1004 1005 |
题意: f n ( x ) = f ( f n − 1 ( x ) ) f 1 ( x ) = f ( x ) x , f ( x ) ∈ [ 1 , n ] f_n(x) = f(f_{n-1}(x)) \quad f_1(x) = f(x) \qquad x,f(x) ∈[1,n] fn(x)=f(fn−1(x))f1(x)=f(x)x,f(x)∈[1,n]
问 g ( x ) = lim m − > ∞ 1 m ∑ i = 1 m f i ( x ) 对 x ∈ [ 1 , n ] 是 否 相 同 g(x) = \lim_{m -> ∞} \frac{1}{m}\sum^m_{i=1}f_i(x) \qquad 对x ∈[1,n]是否相同 g(x)=limm−>∞m1∑i=1mfi(x)对x∈[1,n]是否相同
转化题意: i i i向 a [ i ] a[i] a[i]连边,问每一个环均值是否相等(进入环的那部分不算在内)
思路:拓扑排序搞掉进入环前的一部分,然后判断
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 1e5+50;
int T, n;
int a[MAXN], d[MAXN];
queueq;
int main()
{
cin >> T;
while(T--)
{
while(!q.empty()) q.pop();
memset(d, 0, sizeof(d));
cin >> n;
for(int i = 1;i <= n;i++) {
scanf("%d", &a[i]);
d[a[i]]++;
}
// 处理进入环之前的无关节点
for(int i = 1;i <= n;i++){
if(d[i] == 0) q.push(i);
}
while(!q.empty())
{
int u = q.front();
q.pop();
d[a[u]]--;
if(d[a[u]] == 0) q.push(a[u]);
}
bool flag = true;
ll sum1 = -1, cnt1 = -1;
for(int i = 1;i <= n;i++)
{
if(d[i] == 0) continue;
ll sum2 = 0, cnt2 = 0;
for( ; d[i]; i = a[i])
{
d[i] = 0;
sum2 += i;
cnt2++;
}
if(sum1 == -1 && cnt1 == -1) {
sum1 = sum2;
cnt1 = cnt2;
}
else {
if(sum1 * cnt2 != sum2 * cnt1) {
flag = false;
break;
}
}
}
if(flag) puts("YES");
else puts("NO");
}
return 0;
}
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 10010;
int T, k;
double x, y, x_1, y_1, x_2, y_2, h, w;
double poww[MAXN];
void init()
{
poww[0] = 1.0;
for(int i = 1;i <= 10001;i++) poww[i] = poww[i-1] * 0.5;
}
int main()
{
init();
scanf("%d", &T);
while(T--)
{
cin >> k;
scanf("%lf%lf%lf%lf%lf%lf", &x, &y, &x_1, &y_1, &x_2, &y_2);
h = y - y_1;
w = abs(x_2 - x_1);
double ans;
if(k == 2) ans = w * h / 2;
else {
ans = (2 * (k - 3) + poww[k] * 6 + 1) * w * h;
}
printf("%.3f\n", ans);
}
return 0;
}
#include
#include
using namespace std;
int main()
{
int T;
scanf("%d", &T);
while(T --) {
double a, b;
scanf("%lf%lf", &a, &b);
if(a <= b) printf("N0 M0R3 BL4CK 1CE TEA!\n");
else printf("ENJ0Y YOURS3LF!\n");
}
return 0;
}
题解思路正确,递推公式有误
f [ i ] = f [ m i d − 2 ] + f [ l e n − m i d − 1 ] + 1 f [ 1 ] = f [ 2 ] = 0 g [ i ] = g [ i − 2 ] + 1 g [ 1 ] = 0 h [ i ] = 1 n ∑ i = 1 n g ( i − 2 ) + g ( n − i − 1 ) + 1 = 1 + 2 n ∑ i = 1 n − 2 g ( i ) f[i] = f[mid-2]+f[len-mid-1]+1 \\ f[1] = f[2] = 0 \\ g[i] = g[i-2]+1 \\ g[1] = 0 \\ h[i] = \frac{1}{n} \sum_{i=1}^{n}g(i-2)+g(n-i-1)+1 = 1 + \frac{2}{n}\sum_{i=1}^{n-2}g(i) f[i]=f[mid−2]+f[len−mid−1]+1f[1]=f[2]=0g[i]=g[i−2]+1g[1]=0h[i]=n1i=1∑ng(i−2)+g(n−i−1)+1=1+n2i=1∑n−2g(i)
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MOD = 1e9+7;
const int MAXN = 1e6+50;
int T, n;
int num[MAXN][2], sum[MAXN];
int solve(int len, int type)
{
if(len <= 0) return 0;
if(num[len][type]) return num[len][type];
if(type == 0) {
return num[len][type] = solve(len - 2, 1) + 1; // g[i] = g[i-2]+1
}
else {
int mid = (len + 1) / 2;
return num[len][type] = solve(mid-2, 1) + solve(len-1-mid, 1) + 1;// f[i] = f[mid-2]+f[len-mid-1]+1
}
}
void init()
{
for(int i = 1;i <= 1000000;i++) solve(i, 0);
for(int i = 1;i <= 1000000;i++) sum[i] = (sum[i-1] + num[i][0]) % MOD;
}
int qmi(int a, int b, int mod)
{
int res = 1;
while(b)
{
if(b & 1) res = (ll) res * a % mod;
b >>= 1;
a = (ll)a * a % mod;
}
return res;
}
int main()
{
init();
cin >> T;
while(T--)
{
cin >> n;
int ans = (qmi(n, MOD-2, MOD) * (ll)(n + 2*sum[n-2]) % MOD + MOD) % MOD;
printf("%d\n", ans);
}
return 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GJjChuQH-1629985021698)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210811115024176.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pjyFGnk8-1629985021704)(C:/Users/DELL/AppData/Roaming/Typora/typora-user-images/image-20210811115113170.png)]
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 1e5+50;
const int mod = 998244353;
int T, n;
char s[MAXN];
vector a[28];
ll solve(vector vec)
{
vec.push_back(n + 1);
ll ans = 0, per = 0, dif = 0;
for(int i = 1;i < vec.size();i++)
{
ans = (ans + per * (vec[i] - vec[i - 1])) % mod;
dif = (dif + 2 * vec[i - 1] + vec[i] - vec[i - 1]) % mod; // 差分
per = (per + dif) % mod; // 差分
}
return ans;
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%s", s+1);
n = strlen(s+1);
for(int i = 0;i < 26;i++) a[i].clear();
for(int i = 0;i < 26;i++) a[i].push_back(0);
for(int i = 1;i <= n;i++)
{
a[s[i] - 'a'].push_back(i);
}
ll ans = 0;
for(int i = 0;i < 26;i++) ans = (ans + solve(a[i])) % mod;
printf("%lld\n", ans);
}
return 0;
}
对每个箱子分别求其生成函数
奇数
1 : 1 + x 1 + x 2 + ⋯ + = 1 1 − x 1: 1+x^1+x^2+\dots+=\frac{1}{1-x} 1:1+x1+x2+⋯+=1−x1
3 : 1 + x 2 + x 4 + ⋯ + = 1 1 − x 2 3:1+x^2+x^4+\dots+=\frac{1}{1-x^2} 3:1+x2+x4+⋯+=1−x21
2 t − 1 : 1 + x t + ⋯ + = 1 1 − x t 2t-1:1+x^t+\dots+=\frac{1}{1-x^t} 2t−1:1+xt+⋯+=1−xt1
偶数:
2 : 1 + x = 1 − x 2 1 − x 2:1+x = \frac{1-x^2}{1-x} 2:1+x=1−x1−x2
4 : 1 + x + x 2 = 1 − x 3 1 − x 4:1+x+x^2=\frac{1-x^3}{1-x} 4:1+x+x2=1−x1−x3
2 t : 1 + x + ⋯ + x t = 1 − x t 1 − x 2t:1+x+\dots+x^t=\frac{1-x^t}{1-x} 2t:1+x+⋯+xt=1−x1−xt
生成函数 f ( x ) = 1 − x n + 1 ( 1 − x ) n + 1 = 1 ( 1 − x ) n + 1 − x n + 1 ( 1 − x ) n + 1 f(x)=\frac{1-x^{n+1}}{(1-x)^{n+1}} = \frac{1}{(1-x)^{n+1}}-\frac{x^{n+1}}{(1-x)^{n+1}} f(x)=(1−x)n+11−xn+1=(1−x)n+11−(1−x)n+1xn+1
问 x m x^m xm的系数
KaTeX parse error: Undefined control sequence: \C at position 5: ans=\̲C̲_{m+n}^m - \C _…
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 2e6+50;
const int mod = 1e9+7;
ll T, n, m;
ll fact[MAXN], infact[MAXN];
ll qmi(ll a, ll b)
{
ll res = 1;
while(b)
{
if(b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
}
void init()
{
fact[1] = 1, infact[1] = 1;
for(int i = 2;i <= MAXN - 20;i++)
{
fact[i] = fact[i - 1] * i % mod;
infact[i] = infact[i - 1] * qmi(i, mod - 2) % mod;
}
}
ll cal(ll m, ll n)
{
if(m < n) return 0; // 注意这个
ll ans = fact[m] * infact[n] % mod * infact[m - n] % mod;
return ans;
}
int main()
{
init();
cin >> T;
while(T--)
{
cin >> n >> m;
ll ans = ((cal(m+n, m) - cal(m-1, n)) % mod + mod) % mod;
printf("%lld\n", ans);
}
return 0;
}
比赛过的(2) | 补题(0) |
---|---|
1006 1003 |
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 1e7+50;
int T, n;
bool vis[MAXN];
int primes[MAXN], f[MAXN], a[MAXN], cnt; // f[i]表示i的质因子个数
void get_primes(int n)
{
for(int i = 2;i <= n - 20;i++)
{
if(!vis[i])
{
primes[cnt++] = i;
f[i] = 1;
}
for(int j = 0;(ll)i * primes[j] <= n;j++)
{
vis[i * primes[j]] = true;
f[i * primes[j]] = f[i] + 1;
if(i % primes[j] == 0) break;
}
}
}
int main()
{
get_primes(MAXN - 20);
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
int ans = 0;
for(int i = 1;i <= n;i++)
{
scanf("%d", &a[i]);
if(a[i] == 1) a[i] = 0;
ans ^= f[a[i]]; // nim博弈
}
if(ans) puts("Alice");
else puts("Bob");
}
return 0;
}
最小生成树中的最大边
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN = 5050;
int T, n;
bool vis[MAXN];
ll d[MAXN];
struct node{
ll x, y;
}N[MAXN];
ll prim()
{
ll res = 0;
for(int i = 0;i < n;i++)
{
int t = -1;
for(int j = 1;j <= n;j++)
{
if(!vis[j] && (t == -1 || d[t] > d[j])) t = j;
}
if(i) res = max(res, d[t]);
vis[t] = true;
for(int j = 1;j <= n;j++)
d[j] = min(d[j], (N[j].x - N[t].x) * (N[j].x - N[t].x) + (N[j].y - N[t].y) * (N[j].y - N[t].y));
}
return res;
}
int main()
{
scanf("%d", &T);
while(T--)
{
memset(vis, false, sizeof(vis));
memset(d, 0x3f, sizeof(d));
scanf("%d", &n);
for(int i = 1;i <= n;i++)
{
ll x, y;
scanf("%lld%lld", &x, &y);
N[i] = {x, y};
}
ll ans = prim();
printf("%lld\n", ans);
}
return 0;
}
比赛过的(2) | 补题(0) |
---|---|
H E |
#include
#include
#include
#include
#include
using namespace std;
int n;
vectorv;
int main()
{
scanf("%d", &n);
while(n)
{
int tmmp = n / 3;
int tmp = n % 3;
if(tmp == 0) v.push_back(6);
else if(tmp == 1) v.push_back(2);
else v.push_back(3);
n /= 3;
if(tmp == 0) n--;
if((tmmp == 1 && tmp == 0) || tmmp == 0) break;
}
for(int i = v.size()-1;i >= 0;i--)
{
printf("%d", v[i]);
}
printf("\n");
return 0;
}
比赛过的(2) | 补题(0) |
---|---|
H F |
#include
#include
#include
#include
using namespace std;
int main()
{
int n;
scanf("%d", &n);
for(int i = 0; i < (1 << n); i ++) {
int cnt = 0;
for(int j = 0; j < n; j ++) {
if(i >> j & 1) {
cnt ++;
}
}
if(cnt & 1) printf("1");
else printf("0");
}
puts("");
return 0;
}
比赛过的(3) | 补题(0) |
---|---|
1002 1003 1007 |
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N = 100010;
int n, m;
ll k;
int a[N];
int main()
{
int T;
scanf("%d", &T);
while(T --) {
scanf("%d%d%lld", &n, &m, &k);
for(int i = 0; i < n * m; i ++) {
scanf("%d", &a[i]);
}
int ans = 0;
if(k == 1) {
for(int i = 0; i < m; i ++) ans = max(ans, a[i]);
}
else if(k & 1) {
ans = 2e9;
for(int i = 0; i < n; i ++) {
int tmp = 0;
for(int j = 0; j < m; j ++) {
tmp = max(tmp, a[i * m + j]);
}
ans = min(ans, tmp);
}
}
else {
for(int j = 0; j < m; j ++) {
int tmp = 2e9;
for(int i = 0; i < n; i ++) {
tmp = min(tmp, a[i * m + j]);
}
ans = max(ans, tmp);
}
}
printf("%d\n", max(ans, a[0]));
}
return 0;
}
#include
#include
#include
#include
using namespace std;
const int MAXN = 5050;
int T, n;
struct aa{
int a, id;
bool operator<(const aa &aaa)
{
return a > aaa.a;
}
}A[MAXN];
int b[MAXN];
int best[MAXN], worst[MAXN];
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
for(int i = 1;i <= n;i++) {
scanf("%d", &A[i].a);
A[i].id = i;
}
for(int i = 1;i <= n;i++) {
scanf("%d", &b[i]);
}
sort(A+1, A+n+1);
for(int i = 1;i <= n;i++)
{
int maxadd = 0, minadd = 0;
int maxscore = A[i].a + b[1], minscore = A[i].a + b[n];
best[A[i].id] = 1;
worst[A[i].id] = n;
// best
for(int j = 2, k = n, x = 1; x <= n && j <= k;x++) // 注意这边遍历1-n
{
if(x == i) continue;
if(A[x].a + b[k] <= maxscore) {
k--;
}
else {
j++;
maxadd++;
}
}
//worst
for(int j = 1, x = n, k = n-1;x >= 1 && j <= k;x--) // 注意这边遍历1-n
{
if(x == i) continue;
if(A[x].a + b[j] > minscore) {
j++;
}
else {
k--;
minadd++;
}
}
best[A[i].id] += maxadd;
worst[A[i].id] -= minadd;
}
for(int i = 1;i <= n;i++)
{
printf("%d %d\n", best[i], worst[i]);
}
}
return 0;
}
/*
3
4
1 1 1 1
5 4 3 2
4
6 5 4 4
15 10 5 1
4
1 1 1 1
1 1 1 1
*/
题意:
q q q次操作,每次操作有四种情况。
L L L:从左端加入一个元素
R R R:从右端加入一个元素
G G G:删除值为x的数
Q Q Q:查询队列中最中间的元素 ⌈ m + 1 2 ⌉ \lceil {\frac{m+1}{2}} \rceil ⌈2m+1⌉
双向队列的妙题(绝妙!)
用双向队列 q l q l ql 和 q r q r qr 分别来存左侧和右侧添的元素,每进行一次操作,都要重新维护两个队列,保证 q r q r qr 的 队首为 m i d m i d mid
这题我觉得最妙的就是,这样处理下来,将要删除的元素在维护更新的过程中就将其排除出去了
(类似数组的滚动操作)
思路的转换
要求第 m i d m i d mid 个,若保证我一直知道 m i d m i d mid ,便可直接输出,只要维护两个队列中元素个数相等即可
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN = 1e7 + 5;
int l, r;
int vis[MAXN];
dequeql, qr;
void up()
{
while(l > r)
{
while(!vis[ql.back()]) ql.pop_back();
qr.push_front(ql.back()); ql.pop_back();
l--; r++;
vis[qr.front()] = 2;
}
while(r > l + 1)
{
while(!vis[qr.front()]) qr.pop_front();
ql.push_back(qr.front()); qr.pop_front();
l++; r--;
vis[ql.back()] = 1;
}
}
signed main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int q;
cin >> q;
int tot = 0;
while(q--)
{
char op[2];
cin >> op;
if(*op == 'L')
{
l++;
vis[++tot] = 1;
ql.push_front(tot);
up();
}
else if(*op == 'R')
{
r++;
vis[++tot] = 2;
qr.push_back(tot);
up();
}
else if(*op == 'G')
{
int x;
cin >> x;
if(vis[x] == 1) l--;
else r--;
vis[x] = 0;
up();
}
else
{
while(vis[qr.front()] == 0) qr.pop_front();
cout << qr.front() << endl;
}
}
return 0;
}
比赛过的(0) | 补题(1) |
---|---|
0 | 1003 |
题意:给n条直线,满足三条直线不共点,任意两条不重合,输出所有可能满足条件的交点个数。
即求 f [ i ] [ j ] f[i][j] f[i][j]表示 i i i条直线能否凑出 j j j个交点。
即是 f [ n ] = ∑ i = 1 n i ∗ ( i − 1 ) + f [ i ] f[n] = \sum_{i=1}^n i*(i-1) + f[i] f[n]=∑i=1ni∗(i−1)+f[i] f [ i ] f[i] f[i]表示可行的 f [ i ] [ j ] f[i][j] f[i][j]
用 b i t s e t bitset bitset优化转移 O ( n 4 / w ) O(n^4/w) O(n4/w)
打表发现答案有相当长的一段连续可行后缀,不可行交点不超过31500
时间可以优化到 O ( 31500 ∗ n 2 / w ) O(31500 * n^2/w) O(31500∗n2/w)
#include
#include
#include
#include
#include
using namespace std;
const int UP = 31500, N = 705;
int T;
bitset f[N];
void init()
{
f[0][0] = 1;
for(int i = 1;i < N - 2;i++)
{
for(int j = 0;j <= i - 1;j++)
{
f[i] |= (f[j] << j * (i - j));
}
}
}
void print(int x)
{
int limit = min(UP - 1, x * (x - 1) / 2);
for(int i = 0; i <= limit;i++) if(f[x][i]) printf("%d ", i);
for(int i = limit + 1; i * 2 <= x * (x - 1); i++) printf("%d ", i);
printf("\n");
}
int main()
{
init();
scanf("%d", &T);
while(T--)
{
int n;
scanf("%d", &n);
print(n);
}
return 0;
}
Bitset容器是用来存放bit位元素的,每个元素只占1bit位,取值为0或者1,因而比较节约存储空间,bitset提供了多种方法操作位容器,使用前添加头文件即可使用。
(一)创建bitset对象:
bitset<1000> b,b为bitset对象,它能容纳1000个bit位,每个元素初值为0。
注:bitset的大小定义时必须确定,并且定义后也不能修改。
(二)设定元素的值:
(1)下标法,b[i],下标i的取值从0到n-1;
(2)b.set(),对b中全部元素设置为1;
(3)b.reset(),对b中全部元素设置为0;
(4)b.set(pos,值),等价于b[pos]=值;
(5)b.reset(pos),等价于b[pos]=0。
(三)输出元素:
(1)逐个输出,采用下标法:cout<
(2)整体输出,即全部输出:cout<
应用一: 优化boolean multiplication
在做dp的时候,有时候会需要将两个dp矩阵相乘,且矩阵的元素都是bool型。
计算矩阵 A ∗ B = C A*B=C A∗B=C
C [ i ] [ j ] = 1 C[i][j]=1 C[i][j]=1当且仅当存在 k k k, A [ i ] [ k ] = 1 A[i][k]=1 A[i][k]=1&& B [ k ] [ j ] = 1 B[k][j]=1 B[k][j]=1 。
直接算需要 O ( n 3 ) O(n^3) O(n3)的时间。可以用bitset 优化常数。 做法如下:
bitset A[N],B[N],C[N];
void Multi(bitset A[],bitset B[],bitset C[])
{
for (int i=0;i
原理
如果 A [ i ] [ j ] = 1 A[i][j]=1 A[i][j]=1, C [ i ] [ k ] ∣ = A [ i ] [ j ] C[i][k] |= A[i][j] C[i][k]∣=A[i][j] & B [ j ] [ k ] B[j][k] B[j][k] <-> C [ i ] [ k ] ∣ = B [ j ] [ k ] C[i][k] |= B[j][k] C[i][k]∣=B[j][k]
相当于把B的第 j j j行拿去和C的第 i i i行做一次或操作。
http://codeforces.com/contest/781/problem/D