oj: 牛客
A.组队分配(签到题)
oj: 牛客
题解
排序后直接输出。
代码
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
inline int read() {
int 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 * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int n;
vector< pair<int, string> > a;
void init() {
a.resize(n * 3);
}
void sol() {
init();
char s[21];
int x;
_for(i, n * 3) cin >> a[i].second >> a[i].first;
sort(a.begin(), a.end());
_for(i, n) {
printf("ACM-%d", i);
vector<string> t;
for(int j = 0; j < 3; ++j) t.push_back(a[i * 3 + j].second);
for(int j = 2; j >= 0; --j) cout << " " << t[j];
cout << "\n";
}
}
int main() {
int T = read();
_for(i, T) {
n = read();
sol();
}
return 0;
}
B.两点距离(找规律)
oj: 牛客
题解
打表后可以发现当x和y互质时,输出1,当x==y时,输出0,否则输出2。
代码
#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for ( int i = (s); i <= (t); i++)
#define RP(i,t,s) for ( int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
return a/gcd(a,b)*b;
}
inline int read(){
int s=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=s*10+ch-'0';
ch=getchar();
}
return s*f;
}
int n;
void solve(){
int n=read(),q=read();
while(q--){
int x=read(),y=read();
if(x==y) puts("0");
else if(__gcd(x,y)==1) puts("1");
else puts("2");
}
}
int main(){
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
//debug = 1;
#endif
//time_t beg, end;
//if(debug) beg = clock();
solve();
/*
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
*/
return 0;
}
C.轮到谁了?(找规律)
oj: 牛客
题解
观察后发现是斐波那契数列,直接暴力递推,中间对 m m m 取模。不要忘记最后先加上一个 m m m 再对 m m m 取模,否则会出现负数。
代码
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
inline int read() {
int 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 * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
int main() {
int T = read();
_for(i, T) {
LL n = read(), m = read();
LL a = 1, b = 0, c = 1;
_for(j, n) {
c = (a + b) % m;
a = b, b = c;
}
printf("%lld\n", (c - 1 + m) % m);
}
return 0;
}
D-开心消消乐(点分治)
题解
不难发现是一道点分治的题,关键在于怎么计算选取重心后进行分治的贡献。
思路感觉不是特别难想,但是细节不是特别好处理。
下面转一下来自rank1聚聚的思路
代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
#define PI atan(1.0) * 4
#define rp(i, s, t) for ( int i = (s); i <= (t); i++)
#define RP(i, t, s) for ( int i = (t); i >= (s); i--)
#define sc(x) scanf("%d", &x)
#define scl(x) scanf("%lld", &x)
#define ll long long
#define ull unsigned long long
#define mst(a, b) memset(a, b, sizeof(a))
#define lson rt << 1, l, k
#define rson rt << 1 | 1, k + 1, r
#define pii pair<int, int>
#define pll pair<ll, ll>
#define pil pair<int, ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f3f
#define dg if (debug)
#define pY puts("YES")
#define pN puts("NO")
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
#define outval2(a, b) cout << "Debuging...|" << #a << ": " << a << "\t" << #b << ": " << b << "\n";
#define outval3(a, b, c) cout << "Debuging...|" << #a << ": " << a << "\t" << #b << ": " << b << "\t" << #c << ": " << c << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a, ll b)
{
return b ? gcd(b, a % b) : a;
}
ll lcm(ll a, ll b)
{
return a / gcd(a, b) * b;
}
inline int read()
{
int s = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
s = s * 10 + ch - '0';
ch = getchar();
}
return s * f;
}
const int N = 1e5 + 7;
int head[N*2], to[N * 2], val[N*2], nex[N*2], tot;
int sim[N], mxson[N];
int vis[N];
int MX, root, dis[N], summar;
int n, k, cnt, S;
int tin[N],tout[N],pla[N],tim,dep[N],col[N],tl;
ll w[N],w1[N],w2[N],ans,tmp[N];
ll sta[N],sta2[N];
vector<pii> G[N];
void init(){
tot = 0;
rp(i, 1, n) head[i] = -1, vis[i] = col[i]=0;
rp(i,1,n) G[i].clear();
}
int power(int d, int l){
return l >= 3 ? 0 : d * l;
}
void addedge(int u, int v, int w){
val[tot] = w;
to[tot] = v;
nex[tot] = head[u];
head[u] = tot++;
G[u].p_b(m_p(v,w));
}
void getroot(int u, int fa){//选取进行dfs的根(重心)
sim[u] = 1;
mxson[u] = 0;
for (int i = 0; i<G[u].size(); i++){
int v = G[u][i].first;
if (v == fa || vis[v]) continue;
getroot(v, u);
sim[u] = sim[u] + sim[v];
mxson[u] = max(sim[v], mxson[u]);
}
mxson[u] = max(mxson[u], S - sim[u]);
if (mxson[u] < MX){
root = u;
MX = mxson[u];
}
}
void dfs(int u,int ff,ll s,int d,int l)//预处理
{
/*
u:当前节点 ff:父亲节点
s:上一个节点与根节点的val值,即val(root,u)
d:上一个节点的颜色
l:之前颜色的长度
*/
tin[u] = ++tim,pla[tim] = u,w[tim] = s+power(d,l);
dep[u] = dep[ff]+1;
if(dep[u]!=l+1) w1[u]=w1[ff];
else w1[u] = power(d,l);
for(int i=0;i<G[u].size();i++){
int v = G[u][i].first;
if(v==ff||vis[v])continue;
if(!ff) col[v]=G[u][i].second;
else col[v]=col[u];
if(G[u][i].second!=d) dfs(v,u,s+power(d,l)-k,G[u][i].second,1);
else dfs(v,u,s-k,d,l+1);
}
tout[u] = tim;
}
ll calc(int u,int ff,ll s,int d,int l){//计算当前答案
dep[u] = (ff!=0);tim = 0;
dfs(u,ff,s,d,l);
ll ret = 0;int lw;
for(int i=1;i<=tim;i++) w2[i] = w[i] - w1[pla[i]];
for(int i=1;i<=tim;i++){//计算
if(col[pla[i]]==w1[pla[i]]) sta[++tl] = w[i],sta2[tl] = w2[i];
if(i==tim||col[pla[i]]!=col[pla[i+1]]){
sort(sta+1,sta+tl+1);
int lk = 1,rk = tl+1;//w(1,1)
for(;lk<=tl;lk++){
while(rk-1>=1&&sta[lk]+sta[rk-1]>=0)rk--;
ret += tl+1-max(rk,lk+1);
}
sort(sta2+1,sta2+tl+1);
lk = 1,rk = tl+1;//w2(1,1)
for(;lk<=tl;lk++){
while(rk-1>=1&&sta2[lk]+sta2[rk-1]>=0)rk--;
ret -= tl+1-max(rk,lk+1);
}
tl = 0;
}
}
lw = (ff==0);//w(a,b)
for(int i=lw+1;i<=tim;i++){
if(i==tim||col[pla[i]]!=col[pla[i+1]]){
sort(w+lw+1,w+i+1);
int lk = lw+1,rk = i+1;
for(;lk<=i;lk++){
while(rk-1>=lw+1&&w[lk]+w[rk-1]>=0)rk--;
ret -= i+1-max(rk,lk+1);
}
lw = i;
}
}
lw = (ff==0);//w2
for(int i=lw+1;i<=tim;i++){
if(i==tim||col[pla[i]]!=col[pla[i+1]]){
sort(w2+lw+1,w2+i+1);
int lk = lw+1,rk = i+1;
for(;lk<=i;lk++){
while(rk-1>=lw+1&&w2[lk]+w2[rk-1]>=0)rk--;
ret += i+1-max(rk,lk+1);
}
lw = i;
}
}
sort(w+1,w+1+tim);//w
int lk = 1,rk = tim+1;
for(;lk<=tim;lk++){
while(rk-1>=1&&w[lk]+w[rk-1]>=0)rk--;
ret += tim+1-max(rk,lk+1);
}
return ret;
}
void Divide(int tr){//分治
ans = ans + calc(tr,0,0,0,0);
vis[tr] = 1;
for (int i = 0;i<G[tr].size(); i ++){
int v = G[tr][i].first;
if (vis[v]) continue;
ans = ans - calc(v,tr,-k,G[tr][i].second,1);
S = sim[v];
root = 0;
MX = INF;
getroot(v, 0);
Divide(root);
}
}
void solve(){
n = read(), k = read();
init();
rp(i, 1, n - 1){
int u = read(), v = read(), w = read();
addedge(u, v, w);
addedge(v, u, w);
}
rp(i,1,n){
sort(G[i].begin(),G[i].end(),[&](pii a,pii b){
return a.second<b.second;
});
}
// rp(i,1,n){
// outval(i);
// rp(j,0,G[i].size()-1){
// outval2(G[i][j].first,G[i][j].second);
// }
// }
ans=0;
MX = INF;
S = n;
getroot(1, 0);
Divide(root);
printf("%lld\n", ans);
}
int main()
{
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
//debug = 1;
#endif
//time_t beg, end;
//if(debug) beg = clock();
int T=read();
while(T--) solve();
/*
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
*/
return 0;
}
E.线段树(线段树+公式)
oj: 牛客
题解
这个题的关键在于怎么维护区间
[
l
,
r
]
[l,r]
[l,r]数字的乘积和。
转换为数学公式为
∑
i
=
l
r
a
i
∑
j
=
i
+
1
r
a
j
\sum_{i=l}^{r}a_{i}\sum_{j=i+1}^{r}a_{j}
∑i=lrai∑j=i+1raj
我们对公式进行推导可以得出
∑
i
=
l
r
a
i
∑
j
=
i
+
1
r
a
j
⟹
∑
i
=
l
r
a
i
∑
j
=
l
r
a
j
−
∑
i
=
l
r
a
i
2
2
\sum_{i=l}^{r}a_{i}\sum_{j=i+1}^{r}a_{j}\Longrightarrow \frac{\sum_{i=l}^{r}a_{i}\sum_{j=l}^{r}a_{j}-\sum_{i=l}^{r}a_{i}^{2}}{2}
i=l∑raij=i+1∑raj⟹2∑i=lrai∑j=lraj−∑i=lrai2
因此我们只需要维护一下区间平方和+区间和就行了。
直接魔改一下hdu4578的模板代码
最后的时候注意一下负数的情况。
代码
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<queue>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
using namespace std;
int debug=0;
ll mod;
ll a[100007];
struct node{
ll v1,v2,mul,add;
}tree[400007];
void pushup(int rt){
tree[rt].v1=(tree[rt<<1].v1+tree[rt<<1|1].v1)%mod;
tree[rt].v2=(tree[rt<<1].v2+tree[rt<<1|1].v2)%mod;
}
void build(int rt, int l, int r){
tree[rt].mul=1;
tree[rt].add=0;
if(l==r){
tree[rt].v1=a[l];
tree[rt].v2=(a[l]*a[l])%mod;
return ;
}
int m=(l+r)/2;
build(lson);
build(rson);
pushup(rt);
}
void pushdown(int rt, int l, int r){
int m=(l+r)/2;
ll Lv1=tree[rt<<1].v1,Rv1=tree[rt<<1|1].v1;
ll Lv2=tree[rt<<1].v2,Rv2=tree[rt<<1|1].v2;
ll a=tree[rt].mul,b=tree[rt].add;
tree[rt<<1].v2=(a*a%mod*(Lv2)%mod+2ll*a%mod*b%mod*Lv1%mod+b*b%mod*(m-l+1)%mod)%mod;
tree[rt<<1|1].v2=(a*a%mod*(Rv2)%mod+2ll*a%mod*b%mod*Rv1%mod+b*b%mod*(r-m)%mod)%mod;
tree[rt<<1].v1=(tree[rt<<1].v1*tree[rt].mul+tree[rt].add*(m-l+1))%mod;
tree[rt<<1|1].v1=(tree[rt<<1|1].v1*tree[rt].mul+tree[rt].add*(r-m))%mod;
tree[rt<<1].mul=(tree[rt<<1].mul*tree[rt].mul)%mod;
tree[rt<<1|1].mul=(tree[rt<<1|1].mul*tree[rt].mul)%mod;
tree[rt<<1].add=(tree[rt<<1].add*tree[rt].mul+tree[rt].add)%mod;
tree[rt<<1|1].add=(tree[rt<<1|1].add*tree[rt].mul+tree[rt].add)%mod;
tree[rt].mul=1;
tree[rt].add=0;
return ;
}
void update1(int rt, int stdl, int stdr, int l, int r, ll k){
if(r<stdl || stdr<l){
return ;
}
if(l<=stdl && stdr<=r){
tree[rt].v2=(tree[rt].v2+2ll*tree[rt].v1%mod*k%mod+(stdr-stdl+1)*k%mod*k%mod)%mod;
tree[rt].v1=(tree[rt].v1+k*(stdr-stdl+1)%mod)%mod;
tree[rt].add=(tree[rt].add+k)%mod;
return ;
}
pushdown(rt, stdl, stdr);
int m=(stdl+stdr)/2;
update1(rt<<1, stdl, m, l, r, k);
update1(rt<<1|1, m+1, stdr, l, r, k);
pushup(rt);
return ;
}
void update2(int rt, int stdl, int stdr, int l, int r, ll k){
if(r<stdl || stdr<l){
return ;
}
if(l<=stdl && stdr<=r){
tree[rt].v2=tree[rt].v2*k%mod*k%mod;
tree[rt].v1=(tree[rt].v1*k)%mod;
tree[rt].mul=(tree[rt].mul*k)%mod;
tree[rt].add=(tree[rt].add*k)%mod;
return ;
}
pushdown(rt, stdl, stdr);
int m=(stdl+stdr)/2;
update2(rt<<1, stdl, m, l, r, k);
update2(rt<<1|1, m+1, stdr, l, r, k);
pushup(rt);
return ;
}
ll query1(int rt, int stdl, int stdr, int l, int r){
if(r<stdl || stdr<l){
return 0;
}
if(l<=stdl && stdr<=r){
return tree[rt].v1;
}
pushdown(rt, stdl, stdr);
int m=(stdl+stdr)/2;
return (query1(rt<<1, stdl, m, l, r)+query1(rt<<1|1, m+1, stdr, l, r))%mod;
}
ll query2(int rt, int stdl, int stdr, int l, int r){
if(r<stdl || stdr<l){
return 0;
}
if(l<=stdl && stdr<=r){
return tree[rt].v2;
}
pushdown(rt, stdl, stdr);
int m=(stdl+stdr)/2;
return (query2(rt<<1, stdl, m, l, r)+query2(rt<<1|1, m+1, stdr, l, r))%mod;
}
ll quickPow(ll a, ll b, ll mod) {
ll ans = 1;
while(b) {
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
void solve(){
int n, m;
scanf("%d%d%lld", &n, &m, &mod);
ll inv2=quickPow(2,mod-2,mod)%mod;
for(int i=1; i<=n; i++) scanf("%lld", &a[i]);
build(1, 1, n);
while(m--){
int chk;
scanf("%d", &chk);
int x, y;
ll k;
if(chk==1){
scanf("%d%d%lld", &x, &y, &k);
update1(1, 1, n, x, y, k);
ll sum1=query1(1, 1, n, x, y);
ll sum2=query2(1, 1, n, x, y);
// cout<<sum1<<" "<<sum2<<endl;
}
else if(chk==2){
scanf("%d%d%lld", &x, &y, &k);
update2(1, 1, n, x, y, k);
}
else{
scanf("%d%d", &x, &y);
ll sum1=query1(1, 1, n, x, y);
ll sum2=query2(1, 1, n, x, y);
// cout<<sum1<<" "<<sum2<<endl;
printf("%lld\n",(sum1*sum1%mod-sum2+mod)*inv2%mod);
}
}
}
int main(){
//freopen("in.txt", "r", stdin);
//debug = 1;
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
最长回文串(找规律)
oj: 牛客
题解
只要两个字符串中的每一个字符的出现次数都一样,我们就认为这两个字符串是一样的。
只要一个串的出现次数t大于1,那么这个串就一定能作为回文串的两端中的某一段,并且出现偶数次。对答案的贡献是 2 m ⌊ t 2 ⌋ 2m\lfloor \frac{t}{2}\rfloor 2m⌊2t⌋。
我们在出现次数为1的串里找一个这样的串: 串内出现次数为奇数的字符 的个数不超过1。这样我们就能把这个串作为回文中心。对答案的贡献是 m m m。
代码
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
inline int read() {
int 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 * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
map< string, int > mp;
int n, m;
void init() {
mp.clear();
}
int che(string s) {
map<char, int> tem;
for(char c : s) ++tem[c];
int cnt = 0;
for(auto i : tem) {
cnt += (i.second & 1);
}
return cnt <= 1;
}
void sol() {
init();
string s;
_for(i, n) {
cin >> s;
sort(s.begin(), s.end());
++mp[s];
}
int ans = 0, f = 0;
for(auto i : mp) {
if(i.second > 1) ans += i.second / 2 * 2 * m;
else if(f == 0) {
if(che(i.first)) {
ans += m;
f = 1;
}
}
}
printf("%d\n", ans);
}
int main() {
n = read(), m = read();
sol();
return 0;
}
G.管管的幸运数字(素数)
oj: 牛客
题解
由于询问的n的值不超过 10000 10000 10000,所以我们筛出 15000 15000 15000 以内的素数就足够了。
先判断 n n n 是不是素数,这一步可以合并到寻找不小于 n n n 的最小的素数中。
分别找出不小于 n n n 的最小的素数和比 n n n 小的最大的素数。
不小于
n
n
n 的最小的素数是 *lower_bound(arr.begin(), arr.end(), d)
。判断值是否与
n
n
n 相等。
比
n
n
n 小的最大的素数是 *upper_bound(arr.begin(), arr.end(), d)
。
如果 n n n 不是素数,则二者与 n n n 的距离取最小值。
代码
#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
inline int read() {
int 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 * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
vector<int> arr;
int vis[1000006];
void doit(int maxnum) {
for(int i = 2; i <= maxnum; ++i) {
if(!vis[i]) arr.push_back(i);
for(int j = 0; j < arr.size() && arr[j] * i <= maxnum; ++j) {
vis[arr[j] * i] = 1;
if(i % arr[j] == 0) break;
}
}
}
int main() {
doit(15000);
int T = read();
_for(i, T) {
int d = read();
int p = lower_bound(arr.begin(), arr.end(), d) - arr.begin();
if(d == arr[p]) printf("YES\n");
else {
int pp = upper_bound(arr.begin(), arr.end(), d) - arr.begin() - 1;
printf("%d\n", min(d - arr[pp], arr[p] - d));
}
}
return 0;
}
H-鸽子的浮点运算
题解
一道不算复杂的模拟题,直接根据题意模拟即可。
首先写一个函数calc把读入的数转换成16位的二进制表示。
注意在乘法和加法运算前需要先把读入的数转换成16位浮点数之后再进行加减法。
我采用的方法是先调用calc函数把读入的数转换成二进制表示,然后再写一个to16函数把转换后的二进制表示再转换成16位浮点数,然后根据需要选择进行加法或者乘法,最后再把运算后的数转换成二进制表示输出。
代码
#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for ( int i = (s); i <= (t); i++)
#define RP(i,t,s) for ( int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define pY puts("YES")
#define pN puts("NO")
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
return a/gcd(a,b)*b;
}
inline int read(){
int s=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=s*10+ch-'0';
ch=getchar();
}
return s*f;
}
const int N = 1e5+7;
const double eps = 1e-9;
int a[N];
string to2(int x){
int base=1;while(base<=x) base*=2;
string tmp="";
int f=0;
while(base>0){
if(x>=base){
tmp+="1";
x-=base;
f=1;
}
else if(f==1) tmp+="0";
base/=2;
}
return tmp;
}
string calc(double num){
string t1="0";
num=fabs(num);
int n1=(int)num;
double n2=num-n1;
// cout<<n1<<" "<<n2<<endl;
if(n1==0){
string tt="";
double ans=0.5;
while(n2>0){
if(n2>ans||fabs(n2-ans)<=eps){
// cout<<n2<<" "<<ans<<endl;
tt+="1";
n2-=ans;
}
else tt+="0";
ans/=2.0;
}
int len=tt.size();
string t3="";
int cnt=0;
rp(i,0,len-1){
if(tt[i]=='1'){
t3=tt.substr(i+1);
cnt++;
break;
}
else cnt++;
}
// outval(tt);
// outval(cnt);
string t2=to2(15-cnt);
if(t2.size()<5){
reverse(t2.begin(),t2.end());
while(t2.size()<5) t2+="0";
reverse(t2.begin(),t2.end());
}
while(t3.size()>10) t3.erase(prev(t3.end()));
while(t3.size()<10) t3+="0";
// if(t3[t3.size()-1]=='1') t3[t3.size()-1]='0';
// outval(t3);
return t1+t2+t3;
}
string tmp=to2(n1);
int cnt=tmp.size()-1;
string t2=to2(15+cnt);
if(t2.size()<5){
reverse(t2.begin(),t2.end());
while(t2.size()<5) t2+="0";
reverse(t2.begin(),t2.end());
}
string t3="";
if(tmp.size()>1) t3=tmp.substr(1);
string tt="";
double ans=0.5;
int ct=0;
while(n2>0){
if(n2>ans||fabs(n2-ans)<=eps){
tt+="1";
n2-=ans;
}
else tt+="0";
ans/=2.0;
ct++;
}
t3+=tt;
while(t3.size()>10) t3.erase(prev(t3.end()));
while(t3.size()<10) t3+="0";
// if(t3[t3.size()-1]=='1') t3[t3.size()-1]='0';
// cout<<t1<<" "<<t2<<" "<<t3<<endl;
return t1+t2+t3;
}
int toInt10(string s){
reverse(s.begin(),s.end());
int len=s.size();
int ans=0;
int base=1;
rp(i,0,len-1){
if(s[i]=='1') ans+=base;
base*=2;
}
return ans;
}
double toDouble10(string s){
double base=0.5;
int len=s.size();
double ans=0;
rp(i,0,len-1){
if(s[i]=='1') ans+=base;
base/=2;
}
return ans;
}
double to16(double x){
string ss=calc(x);
double ans=0;
int num=toInt10(ss.substr(1,5));
if(num<15){
int cnt=15-num-1;
// outval(ss);
string tt=ss.substr(6);
reverse(tt.begin(),tt.end());
tt+="1";
int num=0;while(num<cnt) tt+="0",num++;
reverse(tt.begin(),tt.end());
// outval(tt);
ans+=toDouble10(tt);
}
else{
// outval(ss);
int cnt=num-15;
string t2=ss.substr(6,cnt);
ans+=toInt10("1"+t2);
string tt=ss.substr(6+cnt,10-t2.size());
// outval(tt);
ans+=toDouble10(tt);
}
// outval(ans);
return ans;
}
void solve(){
int op=read();
double a,b;scanf("%lf%lf",&a,&b);
// cout<<a*b<<endl;
if(op==1) cout<<calc(a)<<endl;
else if(op==2) cout<<calc(to16(a)+to16(b))<<endl;
else cout<<calc(to16(a)*to16(b))<<endl;
}
int main(){
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
//debug = 1;
#endif
//time_t beg, end;
//if(debug) beg = clock();
int T=read();
while(T--) solve();
/*
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
*/
return 0;
}
I.鸽子的整数运算(签到题)
oj: 牛客
题解
签到题,直接根据题意模拟即可
代码
#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for (register int i = (s); i <= (t); i++)
#define RP(i,t,s) for (register int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
return a/gcd(a,b)*b;
}
inline int read(){
int s=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=s*10+ch-'0';
ch=getchar();
}
return s*f;
}
void solve(){
int op=read(),a=read(),b=read();
if(op==1) cout<<a+b<<endl;
else if(op==2) cout<<a-b<<endl;
else if(op==3) cout<<a*b<<endl;
else cout<<a/b<<endl;
}
int main(){
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
//debug = 1;
#endif
//time_t beg, end;
//if(debug) beg = clock();
int T=read();
while(T--) solve();
/*
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
*/
return 0;
}
J.鸽者文明的三体问题(计算几何)
oj: 牛客
题解
直接暴力枚举判断点是不是在三角形内即可
判断方法:用该点和三角形的每一条边进行叉积判断是否在所有边的相同侧。
代码
#include <bits/stdc++.h>
#define PI atan(1.0)*4
#define rp(i,s,t) for ( int i = (s); i <= (t); i++)
#define RP(i,t,s) for ( int i = (t); i >= (s); i--)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define ll long long
#define ull unsigned long long
#define mst(a,b) memset(a,b,sizeof(a))
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pil pair<int,ll>
#define m_p make_pair
#define p_b push_back
#define ins insert
#define era erase
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f3f
#define dg if(debug)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n";
using namespace std;
int debug = 0;
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
ll lcm(ll a,ll b){
return a/gcd(a,b)*b;
}
inline int read(){
int s=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=s*10+ch-'0';
ch=getchar();
}
return s*f;
}
struct node{
int x[3],y[3];
}p[1007];
int xx,yy;
int judge(int x1,int y1,int x2,int y2){
return x1*y2-x2*y1;
}
int check(int id){
int f1=judge(xx-p[id].x[1],yy-p[id].y[1],p[id].x[2]-p[id].x[1],p[id].y[2]-p[id].y[1]);
int f2=judge(xx-p[id].x[2],yy-p[id].y[2],p[id].x[0]-p[id].x[2],p[id].y[0]-p[id].y[2]);
int f3=judge(xx-p[id].x[0],yy-p[id].y[0],p[id].x[1]-p[id].x[0],p[id].y[1]-p[id].y[0]);
return ((f1>0)==(f2>0)&&(f2>0)==(f3>0));
}
void solve(){
int n=read(),q=read();
rp(i,1,n){
p[i].x[0]=read();
p[i].y[0]=read();
p[i].x[1]=read();
p[i].y[1]=read();
p[i].x[2]=read();
p[i].y[2]=read();
}
rp(i,1,q){
xx=read(),yy=read();
int cnt=0;
rp(i,1,n) if(check(i)) cnt++;
if(cnt&1) puts("Yes");
else puts("No");
}
}
int main(){
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
//debug = 1;
#endif
//time_t beg, end;
//if(debug) beg = clock();
solve();
/*
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
*/
return 0;
}
K.xor
oj: 牛客
题解
动态规划。
再回顾一下题意:将数组 a a a 分成若干彼此互不相交的组,每组的异或和为 x x x。求分组的划分数。
思考一个问题:任何异或和为 x x x 的区间都能自成一组吗?
显然不是,例如当
x
x
x 为
1
1
1 时的2 3 2 3
中的3 2
,虽然异或和为
1
1
1,但如果把3 2
分为一组,两边的2
和3
就无处安放了。正确的分法应该是2 3
和2 3
。
那应该怎么分呢?
考虑将
a
a
a 从左到右贪心地分成若干个足够小的异或和为x的组,数组内每一个元素都在一个这样的组内。例如当
x
x
x 为
1
1
1 时,
a
a
a 为1 2 2 1 2 2 1
时,我们将数组分为1
, 2 2 1
, 2 2 1
。
又发现分出的组还可以再分出一些足够小的异或和为0的组,将
a
a
a 进一步划分为:1
, 2 2
, 1
, 2 2
, 1
。再例如1 2 2 2 2 1
分为1
, 2 2
, 2 2
, 1
。之所以划分出异或和为
0
0
0 的组,是因为当他们出现在两个异或和为
1
1
1 的组之间时,它们既可以合并到左边的组,也可以合并到右边的组,所以他们的数量值得我们关注。
为了方便下文叙述,我们将 异或和为 x x x 的组 称为 u x ux ux,将 异或和为 0 0 0 的组 称为 u 0 u0 u0,将 把数组 a a a 分成若干彼此互不相交的组且每组的异或和为 x x x 的划分数 称为 划分数。
这样分完之后,所有的合理的对数组 a a a 的划分都是 u x ux ux 和 u 0 u0 u0 组合的结果。而且每次必须选奇数个 u x ux ux 合并到一起,不然每个 u x ux ux 的值为 x x x,偶数个 x x x 异或之后为 0 0 0,而不是 x x x。
为了方便进行状态转移,我们在
u
x
ux
ux 中间插入
u
0
u0
u0 的数量,如果不存在
u
0
u0
u0 就插入
0
0
0。我们把这个数组叫做数组
b
b
b。例如将1 2 2 2 2 1
表示为ux 2 ux
,将1 2 2 1 2 2 1 1
表示为ux 1 ux 1 ux 0 ux
。对于数组两端的
u
0
u0
u0,我们是不考虑的。因为它们只能被合并到相邻的
u
x
ux
ux 而不能单独存在,对答案没有贡献,所以就当它们没有出现过就行了。
然后定义 d p [ i ] dp[i] dp[i] 为 b [ i : ] b[i:] b[i:](从 i i i 到末尾)的划分数。且只考虑 i i i 为偶数的情况( i i i 从 0 0 0 开始计数)之所以不考虑 i i i 为奇数的情况是因为奇数位置对应的都是 u 0 u0 u0 的个数,上面也说了以 u 0 u0 u0 作为数组边界时时可以将其视作不存在,所以 d p [ 偶 数 ] dp[偶数] dp[偶数] 就和 d p [ 偶 数 + 1 ] dp[偶数+1] dp[偶数+1] 一样了,没必要重复计算。
边界:
- 考虑 b b b 数组最后一个 u x ux ux,答案时 1 1 1。 d p [ − 1 ] = 1 dp[-1] = 1 dp[−1]=1(-1代表最后一个元素,后面以此类推)。
- 考虑 b b b 数组最后两个 u x ux ux,不能执行合并操作。中间有 d p [ − 2 ] dp[-2] dp[−2] 个 u 0 u0 u0,可以划分 d p [ − 2 ] dp[-2] dp[−2] 个 u 0 u0 u0 到左边,其他的划分到右边,也可以划分 d p [ − 2 ] − 1 dp[-2]-1 dp[−2]−1 个到左边,也可以划分 d p [ − 2 ] − 2 dp[-2]-2 dp[−2]−2 个…,划分法就有 d p [ − 2 ] + 1 dp[-2]+1 dp[−2]+1 种。
转移:
- 第 i i i 个状态有两种转移方式,其一是将 b [ i ] b[i] b[i] 与 b [ i + 2 ] b[i+2] b[i+2] 合并,但是由于每次合并必须是奇数个 u x ux ux,所以将 b [ i ] b[i] b[i] 合并到 b [ i + 2 ] b[i+2] b[i+2] 后的组合数实际上是由 b [ i + 4 ] b[i+4] b[i+4] 决定的。可以认为把 b [ i ] b[i] b[i], b [ i + 2 ] b[i+2] b[i+2], b [ i + 4 ] b[i+4] b[i+4] 绑定到了一起(中间的 u 0 u0 u0 也是绑在一起),这样就可以把他们仨看成只有一个 b [ i + 4 ] b[i+4] b[i+4]。所以此时 d p [ i ] + = d p [ i + 4 ] dp[i]+=dp[i+4] dp[i]+=dp[i+4]。
- 其二是将 b [ i ] b[i] b[i] 单独作为一个组(可能前面会有别的组选择和 b [ i ] b[i] b[i] 合在一起,但此时不考虑前面的),这时 b [ i ] b[i] b[i] 和 b [ i + 2 ] b[i+2] b[i+2] 中间有 b [ i + 1 ] b[i+1] b[i+1] 个 u 0 u0 u0,一共有 b [ i + 1 ] + 1 b[i+1]+1 b[i+1]+1 种划分方式(参考边界.2)。每种又对应 d p [ i + 2 ] dp[i+2] dp[i+2] 种,所以 d p [ i ] + = ( d p [ i + 1 ] + 1 ) ∗ d p [ i + 2 ] dp[i] += (dp[i + 1] + 1) * dp[i + 2] dp[i]+=(dp[i+1]+1)∗dp[i+2]。
综上, d p [ i ] = d p [ i + 4 ] + ( d p [ i + 1 ] + 1 ) ∗ d p [ i + 2 ] dp[i] = dp[i + 4] + (dp[i + 1] + 1) * dp[i + 2] dp[i]=dp[i+4]+(dp[i+1]+1)∗dp[i+2]。
代码
#include <bits/stdc++.h>
#define rp(i, s, t) for (int i = (s); i <= (t); i++)
#define RP(i, t, s) for (int i = (t); i >= (s); i--)
#define ll long long
using namespace std;
inline int read() {
int s = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
s = s * 10 + ch - '0';
ch = getchar();
}
return s * f;
}
const int N = 1e6 + 7;
const ll mod = 1e9 + 7;
int a[N];
int n;
vector<ll> dp;
void solve() {
dp.clear();
n = read();
ll x = read();
rp(i, 1, n) a[i] = read();
int num = 0;
ll tem = 0;
int nu0 = 0;
rp(i, 1, n) {
tem ^= a[i];
if(tem == 0) ++nu0;
else if (tem == x) {
if(num == 0) dp.push_back(0);
else {
dp.push_back(nu0);
dp.push_back(0);
}
nu0 = 0;
tem = 0;
++num;
}
}
if(tem || dp.size() == 0) {
printf("0\n");
return;
}
if(dp.size() == 1) {
printf("1\n");
return;
}
dp[dp.size() - 1] = 1;
dp[dp.size() - 3] = dp[dp.size() - 2] + 1;
for(int i = dp.size() - 5; i >= 0; i -= 2) {
dp[i] = dp[i + 4] + (dp[i + 1] + 1) * dp[i + 2];
dp[i] %= mod;
}
printf("%lld\n", dp[0]);
}
int main() {
solve();
return 0;
}
比赛过程
开局zwt在写A,我把签到题I过了,涛哥紧接着把A过了,然后和wjh一起去看B了,打了表发现了规律,交了一发(没改cout并且被评测机抖了一手),意外tle,中间wjh看了G题,给涛哥说质数筛一下就行,涛哥看了一下,写了一会把G过了,之后我们一起来找B的tle原因,在实在想不到原因的情况下,我改成put输出过了,然后开始一起看C,在想到解法后,被负数取余(埋下伏笔)卡了一手,紧接着一起开始看F,在我口胡了大致解法后,涛哥完善了一下,把F给过了,开始看J,我大致想了个叉积的想法后,在涛哥的口胡加持下,把J题过了。这时已经开场两个小时了,我们跟榜看了K题,我和wjh没有其他想法,期间我大致看了E题,看一眼觉得应该是个中后期线段树题,没有细想,但是卡着K题有点难受,就没有仔细想解法。然后开始疯狂自闭,涛哥在三小时半大致想了一个解法,不过没有过,找到反例后,涛哥把bug改了后还是wa,然后涛哥开始循环wa,找反例,debug的过程,这时我已经跟不上涛哥的思路了。在4个小时左右的时候我开始想E题的解法,看了题意发现最难的公式部分之前做过一道类似的题,转换一下,这个题就变成了比较简单的线段树模板题。这时涛哥也发现他之前的做法是错的,想了一个dp的解法。然后我把E题的板子抄上去,debug出样例后交了一发,最后发现没有处理负数的情况,改了改过了,大概不到30s涛哥把K题也过了。这时比赛还剩30分钟,看了看H题,发现是一道模拟题,感觉时间不够就没写了。
总结+反思
zwt
交题前多测几发真的不浪费时间!!!
交题前多测几发真的不浪费时间!!!
交题前多测几发真的不浪费时间!!!
说不定胡乱写组数据就会发现自己不但过不了,甚至解法都不对。。
K题漫长的debug过程中全靠胡乱造数据发现bug,如果我没有造这么多数据,可能到比赛结束我还在死扣一个错误的解法。
wjh
mjh
B题不应该使用比较耗时的cout输出,贡献了一发罚时,还浪费了时间,C题被负数取余卡了一段时间。最后在发现跟不上涛哥的K题思路后(甚至发现K题没思路时),就应该果断开E题,而且E题也被负数取余卡了一手,又贡献了一发罚时,E题罚时应该会少一点,也能剩下时间做H题。