本蒟蒻这次只过了三题 赛后学习了一下出题人巨佬的标码(码风比我好多了 贴的代码有些是仿出题人)
现在将自己的理解写下来与大家分享
A
这个题一分析就是每个数字都会与所有数字&一下 (a&a=a)
&字操作是二进制同位都为一才为一 这时解法就变成统计每个二进制位上1的次数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include
#define ll
long
long
using namespace std;
const
ll maxn = 1e5;
const
ll maxm =
30
;
ll res;
ll arr[maxn +
5
], n, cnt[maxm +
5
];
int
main()
{
scanf(
"%d"
, &n);
for
(
int
i =
1
; i <= n; ++i)scanf(
"%d"
, &arr[i]);
for
(
int
i =
1
; i <= n; ++i)
for
(
int
j =
0
; j <
30
; ++j)
if
(arr[i] & (
1
<< j))cnt[j] +=
1
;
///各位上1的个数
for
(
int
i =
0
; i <
30
; ++i)res += cnt[i] * cnt[i] * (
1
<< i);
printf(
"%lld\n"
, res);
return
0
;
}
|
B
简单的n2枚举 分析一下可以知道每条边都用了n-2次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#include
#define ll
long
long
using namespace std;
ll n,ans;
ll mod=
998244353
;
struct P
{
ll x,y;
}a[
110
];
ll len(P m,P n)
{
return
abs(m.x-n.x)+abs(m.y-n.y);
}
int
main()
{
scanf(
"%lld"
,&n);
for
(
int
i=
1
;i<=n;++i)scanf(
"%lld%lld"
,&a[i].x,&a[i].y);
for
(
int
i=
1
;i<=n;++i)
{
for
(
int
j=i+
1
;j<=n;++j)
{
ans+=(len(a[i],a[j])*(n-
2
))%mod;
}
}
cout<
return
0
;
}
|
C
这是一个比较基础的dp方程的进阶 从n个物品中选k个
dp[i][j]=dp[i-1][j]+dp[i-1][j-1]
i表示到第几个字符了 j是用了几个字符
dp[i-1][j]是不用这个新加的 dp[i-1][j-1]是用这个新加的
这题的关键点是我们这样计算重复了多少字符(本质不同的解释)
其实仔细一想 我们重复计算就是相当于把 输出前面出现的那个符号在那里又多算了一次
多了哪一部分 这个重复符号上次出现的位置的dp值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#include
#define ll
long
long
using namespace std;
const
ll maxn =
1000
;
const
ll mod = 1e9 +
7
;
ll dp[maxn +
5
][maxn +
5
];
ll n, k, pre[maxn +
5
];
///pre记录上次同一个字符的出现位置
char
s[maxn +
5
];
int
main()
{
scanf(
"%d %d"
, &n, &k);
scanf(
"%s"
, s +
1
);
dp[
0
][
0
] =
1
;
///n中选0个为1种情况
for
(
int
i =
1
; i <= n; ++i) {
dp[i][
0
] =
1
;
for
(
int
j =
1
; j <= i; ++j) {
dp[i][j] = dp[i -
1
][j] + dp[i -
1
][j -
1
];
if
(pre[s[i] -
'a'
])dp[i][j] -= dp[pre[s[i] -
'a'
] -
1
][j -
1
];
dp[i][j] %= mod;
}
pre[s[i] -
'a'
] = i;
}
ll res = dp[n][k];
if
(res <
0
)res += mod;
printf(
"%lld\n"
, res);
return
0
;
}
|
D
这题由于蒟蒻(我)追求梦想用n2枚举过了 但赛后还是写了一下正解方程(n枚举应该可以过)
写这题你需要一个 扩展欧几里得模板(能判断是否有整数解) 然后枚举一个数就把题目变成了一个模板题了 前面是n2枚举 后面大概是正解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
#include
#define ll
long
long
using namespace std;
ll a,b,c,k,minl;
int
main()
{
cin>>a>>b>>c>>k;
minl=min(a,min(b,c));
ll s=k/minl;
for
(
int
i=
1
;i<=s;++i)
{
for
(
int
j=
1
;j<=s;++j)
{
ll z1=k-a*i-b*j;
ll z2=k-b*i-c*j;
ll z3=k-a*i-c*j;
if
(z1%c==
0
){cout<
if
(z2%a==
0
){cout<
if
(z3%b==
0
){cout<
}
}
return
0
;
}
#include
#define ll
long
long
using namespace std;
ll a,b,c,k;
void
exgcd(ll a,ll b,ll&x,ll&y)
{
if
(b==
0
)
{
x=
1
;
y=
0
;
return
;
}
exgcd(b,a%b,x,y);
ll t=x;
x=y;
y=t-(a/b)*y;
}
ll gcd(ll a,ll b){
return
b==
0
?a:gcd(b,a%b);
}
int
main()
{
scanf(
"%lld%lld%lld%lld"
,&a,&b,&c,&k);
for
(ll i=
0
;i<=k/c;i++){
ll d=k-i*c;
ll x,y;
exgcd(a,b,x,y);
ll e=gcd(a,b);
if
(d%e!=
0
)
continue
;
///有无解
ll n=d/e;
ll m=b/e;
x*=n;y*=n;
x=(x%m+m)%m;
y=(d-x*a)/b;
///有无整数解
if
(x>=
0
&&y>=
0
){
printf(
"%lld %lld %lld"
,x,y,i);
return
0
;
}
}
return
0
;
}
|
F
这题作为正在学习计算几何的蒟蒻(我) 那肯定是要学习的
做这题的关键是要注意画图才能理解 代码里大部分有注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
#include
#define lowbit(x) x&(-x)
#define ll
long
long
using namespace std;
typedef pair<
int
,
int
> pii;
const
int
maxn=1e5+
7
;
const
int
pi=acos(-
1.0
);
const
double
eps=1e-
9
;
int
read()
///快读
{
int
x=
0
,f=
1
;
char
s=getchar();
for
( ;!isdigit(s);s==
'-'
&& (f=-
1
),s=getchar());
for
( ;isdigit(s);x=x*
10
+s-
48
,s=getchar());
return
x*f;
}
struct point
{
int
x,y,tag;
point( ) { x=y=tag=
0
; }
point( ll _x,ll _y )
{
x=_x,y=_y;
}
bool operator==(
const
point& rhs )
const
{
return
x==rhs.x && y==rhs.y;
}
point operator -(
const
point &rhs )
const
{
return
point(x-rhs.x,y-rhs.y);
}
point operator+(
const
point &rhs )
const
{
return
point(x+rhs.x,y+rhs.y);
}
ll operator * (
const
point &rhs )
const
{
/// 叉积
return
1ll*x*rhs.y-1ll*y*rhs.x;
}
ll dot(
const
point &rhs )
const
{
/// 点积
return
1ll*x+rhs.x+1ll*y*rhs.y;
}
void
get() { x=read(),y=read(); }
}O,p[maxn],w1[maxn],w2[maxn],A,B;
int
n,m1,m2;
inline bool cmpA( point a,point b ) {
return
(a-A)*(b-A)>
0
; }
inline bool cmpB( point a,point b ) {
return
(a-B)*(b-B)>
0
; }
int
c[maxn];
ll ans=
0
;
void
add(
int
x ) {
while
( x int
query(
int
x ,
int
tt=
0
) {
while
( x ) tt+=c[x],x-=lowbit(x);
return
tt;}
void
solve( point *t,
int
m )
{
sort(t+
1
,t+
1
+m,cmpA);
///以A点极角排序
ans+=(1ll*m*(m-
1
))>>
1
;
///先假设所有点对都能与线段相交
for
(
int
i=
1
;i<=m;i++ ) t[i].tag=i;
sort(t+
1
,t+m+
1
,cmpB);
///以B点极角排序
memset(c,
0
,sizeof(c));
for
(
int
i=
1
;i<=m;i++ )
///减去不能与线段相交的点对
{
///这里假设线段点为P、Q 选出来的点对为 A、B 在画图过程中发现
///选出来的能交的点对 B点要以P排序在A前面而且以Q排序也要在A前面
///不是就减去 以树状数组解决这个区间问题
ans-=query(t[i].tag);
add(t[i].tag);
}
}
int
main()
{
n=read(),A.get(),B.get();
for
(
int
i=
1
;i<=n;i++ ) p[i].get();
for
(
int
i=
1
;i<=n;i++ )
{
if
( (A-p[i])*(B-p[i])>0ll ) w1[++m1]=p[i];
///利用叉积把点分为线段上下两部分
else
w2[++m2]=p[i];
}
solve(w1,m1);
///第一种情况 两个点在同一侧
solve(w2,m2);
ans+=1ll*m1*m2;
///第二种情况 两个点在不同侧 还是一样先假设所有点对都能与线段相交
sort(w1+
1
,w1+m1+
1
,cmpA);
///一样 以A点极角排序
sort(w2+
1
,w2+m2+
1
,cmpA);
for
(
int
i=
1
,bas=
1
;i<=m1;i++ )
///减去不能与线段相交的点对
{
///i 上侧 bas 下侧
///画图分析
while
( bas<=m2 && (w1[i]-A)*(w2[bas]-A)>
0
) ++bas;
///叉积判断
ans-=bas-
1
;
}
sort(w1+
1
,w1+m1+
1
,cmpB);
///以B点极角排序
sort(w2+
1
,w2+m2+
1
,cmpB);
for
(
int
i=
1
,bas=
1
;i<=m2;i++ )
{
///一样
while
( bas<=m1 && (w2[i]-B)*(w1[bas]-B)>
0
) ++bas;
ans-=bas-
1
;
}
printf(
"%lld\n"
,ans);
return
0
;
}
|
e题的题解可能在我大佬队友写完后贴出来(这个方向我是真不会).....
看到最后的彩蛋 推荐纯音乐 Like Sunday,Like Rain ^-^