The 14th Jilin Provincial Collegiate Programming Contest部分题解(A,B,C,E,F,G,H,J,L,M)
oj: CodeForces
Problem A.Chord
题解
签到题,根据题目判断一下就行(注意是环)
代码
#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 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;
string s[20]={"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"};
void solve(){
string a,b,c;cin>>a>>b>>c;
int id1,id2,id3;
rp(i,0,11) if(s[i]==a) id1=i;
rp(i,0,11) if(s[i]==b) id2=i;
rp(i,0,11) if(s[i]==c) id3=i;
// outval3(id1,id2,id3);
if((id2-id1+12)%12==4&&(id3-id2+12)%12==3) puts("Major triad");
else if((id2-id1+12)%12==3&&(id3-id2+12)%12==4) puts("Minor triad");
else puts("Dissonance");
}
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;cin>>T;
while(T--) solve();
/*
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
*/
return 0;
}
Problem B.Problem Select
题解
签到题,逆序取一下数字,然后排序输出取前k小数就行了
代码
#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 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;
void solve(){
int n,k;cin>>n>>k;
vector<int> v;v.clear();
rp(i,1,n){
string s;cin>>s;
// outval(s);
int len=s.size();
int id;
rp(j,0,len-1) if(s[j]=='/') id=j;
// outval(id);
int num=0;
rp(j,id+1,len-1) num=num*10+s[j]-'0';
// outval(num);
v.p_b(num);
}
sort(v.begin(),v.end());
rp(i,0,k-1) {
printf("%d%s",v[i],i==k-1?"\n":" ");
}
}
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;cin>>T;
while(T--) solve();
/*
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
*/
return 0;
}
Problem C. String Game(dp)
oj: CodeForces
题意
给你一个 \(s\) 串和 \(t\) 串,求出 \(s\) 中有多少和 \(t\) 相同的子序列。
题解
定义 \(dp[i][j]\) 表示 \(s[:i]\) 中有多少个和 \(t[:j]\) 相同的子序列。
由于 \(s[:i]\) 包含 \(s[:i-1]\) ,所以 \(dp[i][j]\) 包含 \(dp[i-1][j]\) 。
当 \(s[i] == t[j]\) 时, \(dp[i][j]\) 又包含 \(dp[i-1][j-1]\) ,所以有:
边界:
dp[i][0] = dp[i - 1][0] + (s[i] == t[0])
转移:
dp[i][j] += dp[i - 1][j];
if(s[i] == t[j]) dp[i][j] += dp[i - 1][j - 1];
代码
#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;
const int maxn = 5005;
const int mod = 1000000007;
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;
}
LL dp[5005][1005];
char s[maxn], t[maxn];
int n, m;
void init() {
n = strlen(s), m = strlen(t);
_for(i, n) _for(j, m) dp[i][j] = 0;
}
void sol() {
init();
_for(i, n) {
if(i) dp[i][0] += dp[i - 1][0];
if(s[i] == t[0]) {
++dp[i][0];
}
dp[i][0] %= mod;
}
for(int i = 1; i < n; ++i) {
for(int j = 1; j < m; ++j) {
dp[i][j] += dp[i - 1][j];
if(s[i] == t[j]) dp[i][j] += dp[i - 1][j - 1];
dp[i][j] %= mod;
}
}
printf("%lld\n", dp[n - 1][m - 1]);
}
int main() {
while(~scanf("%s%s", s, t)) {
sol();
}
return 0;
}
Problem E. Shorten the Array
oj: CodeForces
题意
给你一个数组,你可以挑选两个相邻的正数 \(x,y\),然后把 \(x,y\) 替换成 \(x\%y\) 或者 \(y\%x\)。你可以执行此操作任意次,求出最后能剩下的最少的数字数量。
题解
考虑一个策略:选出数组中的最小元素 \(x\) ,然后依次取它和它相邻的数字 \(y\) ,然后替换成 \(x\%y\) 。这样数组中相会只剩下 \(x\) 。然后让剩下的 \(x\) 两两相消变成 \(0\) ,这样答案就是 \(\lceil \frac{num(x)}{2} \rceil\)。
但是当数组中出现一个数字 \(y\) 满足 \(y>x\) 并且 \(gcd(x,y)\neq x\) 时,例如 2 2 2 2 3
按照上述解法将会剩下 \(4\) 个 \(2\) ,答案是 \(2\) 。但是观察后发现可以先选出 \(x=2,y=3\) 使之变成 \(y\%x=1\) ,这样数组中的最小元素就变成 \(1\) 了,而且没有其他元素和它一样,这样我们就可以重复以上的策略,将数组中的其他元素全部消去,答案就是 \(1\) 。
所以我们先找出数组中的最小元素,然后遍历数组看是否存在一个不是 \(x\) 的倍数的数,如果存在就代表存在 \(y\) 满足 \(gcd(x,y)\neq x\) 。那么答案就是 \(1\) 。
如果不存在,那么答案就是 \(\lceil \frac{num(x)}{2} \rceil\)。
代码
#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)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
const int maxn = 2000005;
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, a[maxn];
void sol() {
_for(i, n) a[i] = read();
int mi = a[0], num = 0, f = 0;
_for(p, n) mi = min(mi, a[p]);
_for(p, n) {
if(a[p] == mi) ++num;
if(a[p] % mi) {
f = 1;
break;
}
}
printf("%d\n", f ? 1 : (num + 1) / 2);
}
int main() {
int T = read();
_for(i, T) {
n = read();
sol();
}
return 0;
}
Problem F.Queue
题解
这个题第一思路是用树套树维护,毕竟动态逆序对的经典题型,但是在操作时我们发现区间的长度最大为100,那么我们可以暴力模拟即可。
先用树状数组求出逆序对数,然后我们算一下[l+1,r-1]区间内的数对逆序对数的影响(以及自身),并更新逆序对数,同时记录最小值即可。
当交换\(a[l]\)和\(a[r]\)后,
\(i\in[l+1,r-1]\)区间对逆序对数的影响:
1.当\(a[l]>a[i]\)时,逆序对数会减少一。
2.当\(a[i]>a[r]\)时,逆序对数会减少一。
3.当\(a[i]>a[l]\)时,逆序对数会增加一。
4.当\(a[r]>a[i]\)时,逆序对数会增加一。
最后不要忘记考虑自身的影响:
1.当\(a[l]>a[r]\)时,逆序对数会减一。
2.当\(a[l]<a[r]\)时,逆序对数会加一。
trick:注意树状数组处理不了值为0的情况
代码
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define m_p make_pair
#define p_i pair<int, int>
#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)
#define outval(a) cout << "Debuging...|" << #a << ": " << a << "\n"
#define mem(a, b) memset(a, b, sizeof(a))
#define mem0(a) memset(a, 0, sizeof(a))
#define fil(a, b) fill(a.begin(), a.end(), b);
#define scl(x) scanf("%lld", &x)
#define sc(x) scanf("%d", &x)
#define pf(x) printf("%d\n", x)
#define pfl(x) printf("%lld\n", x)
#define abs(x) ((x) > 0 ? (x) : -(x))
#define PI acos(-1)
#define lowbit(x) (x & (-x))
#define dg if(debug)
#define nl(i, n) (i == n - 1 ? "\n":" ")
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
typedef long long LL;
// typedef __int128 LL;
typedef unsigned long long ULL;
const int maxn = 1000005;
const int maxm = 1000005;
const int maxp = 30;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1000000007;
const double eps = 1e-8;
const double e = 2.718281828;
int debug = 0;
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;
}
struct poi {
};
int n, top;
int a[maxn];
LL d[maxn];
inline void insert(int p, int x) {
while(p < maxn) {
d[p] += x;
p += lowbit(p);
}
}
inline LL query(int p) {
LL tem = 0;
while(p>0) {
tem += d[p];
p -= lowbit(p);
}
return tem;
}
void init(){
memset(d,0,sizeof d);
}
void sol() {
init();
_rep(i,1,n) a[i] = read();
_rep(i,1,n) a[i]++;
LL tem = 0;
_rep(i,1,n) {
// outval(i-query(a[i]));
tem += i-1 - query(a[i]);
insert(a[i],1);
}
// _rep(i,1,n) insert(a[i],-1);
LL ans = tem;
// outval(tem);
int m = read();
_rep(i,1,m) {
int l = read(), r = read();
if(l==r) continue;
LL ol = 0, ne = 0;
for(int j = l + 1; j <= r-1; ++j) {
if(a[j] > a[r]) ol++;
if(a[j]<a[l]) ol++;
if(a[j] > a[l]) ++ne;
if(a[j]<a[r]) ne++;
}
tem += ne - ol;
if(a[l]>a[r]) tem--;
else if(a[r]>a[l]) tem++;
ans = min(ans, tem);
swap(a[l], a[r]);
// outval(tem);
}
printf("%lld\n", ans);
}
int main() {
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
// debug = 1;
#endif
int T = read();
_for(i, T) {
n = read();
sol();
}
return 0;
}
Problem G.Matrix
题解
打表找规律,发现答案是分别对n和m进行分解后的结果的成绩
分解过程:
[1,3] 对应结果 1
[4,8] 对应结果 2
[9,15] 对应结果 3
[16,24] 对应结果 4
[25,35] 对应结果 5
[36,48] 对应结果 6
不难发现当区间的左边为\(x^{2}\),答案就是x,因此直接二分枚举小于等于n的最大平方数,再对这个最大平方数开平方就是结果。
代码
#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 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;
void solve(){
ll n,m;scl(n);scl(m);
int l=1,r=1e9;
ll res1=0;
while(l<=r){
int mid=(l+r)/2;
if(1ll*mid*mid<=n){
l=mid+1;
res1=mid;
}
else r=mid-1;
}
ll res2=0;
l=1,r=1e9;
while(l<=r){
int mid=(l+r)/2;
if(1ll*mid*mid<=m){
l=mid+1;
res2=mid;
}
else r=mid-1;
}
// outval2(res1,res2);
printf("%lld\n",res1*res2);
}
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;
}
Problem H.Curious
题解
算是比较常见的反演题,关键在于怎么把题目转换,有两种比较做法,分别是O(nlogn)和O(mlogm),我只会O(mlogm)的做法,下面就讲一下这个做法。
推导可以发现,最终答案可以转换成下面这个公式。
\(\sum_{i=1}^{m}\sum_{j=1}^{m}[gcd(i,j)==x]c_{i}c_{j}\)
再对这个公式进行反演可以得到(算是比较常见的套路)
\(\sum_{i=1}^{m}\sum_{j=1}^{m}[gcd(i,j)==x]c_{i}c_{j}\)
\(\rightarrow
\sum_{i=1}^{\frac{m}{x}}\sum_{j=1}^{\frac{m}{x}}[gcd(i,j)==1]c_{ix}c_{jx}\sum_{d|gcd(i,j)}\mu_{d}\)
\(\rightarrow
\sum_{d|gcd(i,j)}\mu_{d}\sum_{i=1}^{\frac{m}{xd}}c_{ixd}\sum_{j=1}^{\frac{m}{xd}}c_{jxd}\)
\(f(d)=\sum_{i=1}^{\frac{m}{xd}}c_{ixd}\)
因此可以O(mlogm)预处理得到\(g(x)=\sum_{i=1}^{\frac{m}{x}}c_{ix}\),
那么我们就可以预处理出\(f\)函数的值
\(f(d)=g(xd)=\sum_{i=1}^{\frac{m}{xd}}c_{ixd}\)
最终的答案就是
\(ans=\sum_{d=1}^{m/x}\mu_{d}g(xd)^{2}\)
代码
#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 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 =2e5+7;
bool vis[N];
ll prim[N], mu[N], sum[N];
ll c[N],g[N];
void init(int n){
mu[1] = 1;
for (int i = 2; i <= n; i++)
{
if (!vis[i])
{
mu[i] = -1;
prim[++prim[0]] = i;
}
for (int j = 1; j <= prim[0] && i * prim[j] <= n; j++)
{
vis[i * prim[j]] = 1;
if (i % prim[j] == 0)
break;
else
mu[i * prim[j]] = -mu[i];
}
}
}
int n,m,k;
ll Ans(int x){
ll ans=0;
rp(i,1,m/x) ans+=mu[i]*g[i*x]*g[i*x];
return ans;
}
void solve(){
n=read(),m=read(),k=read();
rp(i,0,m) c[i]=f[i]=0;
rp(i,1,n){
int x=read();
c[x]++;
}
rp(t,1,m) rp(i,1,m/t) g[t]+=c[i*t];
while (k--){
int x = read();
printf("%lld\n", Ans(x));
}
}
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();
init(N-7);
int T=read();
while(T--) solve();
/*
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
*/
return 0;
}
Problem J.Situation
题解
算是对抗搜索的经典题
不懂对抗搜索的可以参考:戳我
直接暴力dfs(测试数据不是很大)就行,dfs采用min_max搜索就行。
如果优化的话可以用Alpha-Beta剪枝搜索。
代码
#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 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;
}
struct State{
char s[3][3];
int gethash(){
int ans=0;
rp(i,0,2){
rp(j,0,2){
if(s[i][j]=='.') ans=ans*3+0;
if(s[i][j]=='X') ans=ans*3+1;
if(s[i][j]=='O') ans=ans*3+2;
}
}
return ans;
}
int calc(){
int cnt1=0,cnt2=0;
if(s[1][1]=='O'){//对角线+十字
//对角线
if(s[0][0]=='O'&&s[2][2]=='O') cnt1++;
if(s[0][2]=='O'&&s[2][0]=='O') cnt1++;
//十字
if(s[1][0]=='O'&&s[1][2]=='O') cnt1++;
if(s[0][1]=='O'&&s[2][1]=='O') cnt1++;
}
else{
//对角线
if(s[0][0]=='X'&&s[2][2]=='X') cnt2++;
if(s[0][2]=='X'&&s[2][0]=='X') cnt2++;
//十字
if(s[1][0]=='X'&&s[1][2]=='X') cnt2++;
if(s[0][1]=='X'&&s[2][1]=='X') cnt2++;
}
if(s[0][0]=='O'){//右上
if(s[0][1]==s[0][0]&&s[0][2]==s[0][0]) cnt1++;
if(s[1][0]==s[0][0]&&s[2][0]==s[0][0]) cnt1++;
}
if(s[2][2]=='O'){//左下
if(s[0][2]==s[2][2]&&s[1][2]==s[2][2]) cnt1++;
if(s[2][0]==s[2][2]&&s[2][1]==s[2][2]) cnt1++;
}
if(s[0][0]=='X'){
if(s[0][1]==s[0][0]&&s[0][2]==s[0][0]) cnt2++;
if(s[1][0]==s[0][0]&&s[2][0]==s[0][0]) cnt2++;
}
if(s[2][2]=='X'){
if(s[0][2]==s[2][2]&&s[1][2]==s[2][2]) cnt2++;
if(s[2][0]==s[2][2]&&s[2][1]==s[2][2]) cnt2++;
}
return cnt1-cnt2;
}
int isEnd(){
rp(i,0,2) rp(j,0,2) if(s[i][j]=='.') return 0;
return 1;
}
};
int dp[100007][2];
int dfs(State state,int op){
int st=state.gethash();
if(dp[st][op]!=-INF) return dp[st][op];
if(state.isEnd()) return state.calc();
if(op){//max
rp(i,0,2){
rp(j,0,2){
if(state.s[i][j]=='.'){
State nxt=state;
nxt.s[i][j]='O';
dp[st][op]=max(dp[st][op],dfs(nxt,1-op));
}
}
}
}
else{//min
dp[st][op]=INF;
rp(i,0,2){
rp(j,0,2){
if(state.s[i][j]=='.'){
State nxt=state;
nxt.s[i][j]='X';
dp[st][op]=min(dp[st][op],dfs(nxt,1-op));
}
}
}
}
return dp[st][op];
}
void solve(){
int op;scanf("%d",&op);
State st;
scanf("%s%s%s",st.s[0],st.s[1],st.s[2]);
printf("%d\n",dfs(st,op));
}
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();
rp(i,0,100000) rp(j,0,1) dp[i][j]=-INF;
int T=read();
while(T--) solve();
/*
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
*/
return 0;
}
Problem L.Swimmer
题解
签到题,先求出当前时间是处在从前往后的路程中还是在从后往前的路程中,再根据这两个情况输出对应的答案即可。
代码
#include <bits/stdc++.h>
using namespace std;
int a[1000010];
int main() {
int n,m,q;
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
while(q--){
int p,k;
scanf("%d%d",&p,&k);
long long len=1LL*p*a[k];
if(len<=m){
printf("%lld\n",len);
}else{
if((len/m)%2==0){
printf("%lld\n",1LL*len-1LL*m*(len/m));
}else{
printf("%lld\n",1LL*m-(1LL*len-1LL*m*(len/m)));
}
}
}
return 0;
}
Problem M.Warmup:Upanishad
题解
热身赛上的题,OI上的一道原题。
不过做法很巧妙,完美利用了树状数组的性质,同时又有一些类似于莫队的指针移动的思想。
首先可以需要知道一个结论:
区间出现偶数次数的异或和=区间出现过的数的异或和 ^ 区间出现奇数次的数的异或和
不难发现区间出现奇数次的数的和可以用异或的前缀和维护(因为出现偶数的数异或之后变成了0)。
这样关键就在于怎么维护区间出现过的数的异或和。
首先我们用一个\(pre\)数组记录当前位置之前值等于\(v_{i}\)的最近下标,然后我们把查询的区间进行离线处理(按照右端点升序排序),这样可以保证从左往右只推一遍,时间复杂度为\(O(n)\)。
之后我们在往右走的过程中,如果一个数之前出现过,我们就把它的前面最近的数删除掉(pre数组维护),否则就直接插入树状数组。
查询时根据端点异或出来就行了。
树状数组用来维护区间出现过的数的异或和。
代码
#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 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 = 5e5+7;
int a[N],n,q;
ll lowbit(ll x){
return x&(-x);
}
struct TreeArray {
ll d[N];
void init() { mst(d,0); }
void insert(int p, ll x) {
while(p <= n) {
d[p] ^= x;
p += lowbit(p);
}
}
ll query(int p) {
ll ans = 0;
while(p>0) {
ans ^= d[p];
p -= lowbit(p);
}
return ans;
}
}tree;
ll sum[N];
struct node{
int l,r,pos;
}p[N];
map<int,int> mp;
int pre[N];
ll ans[N];
void init(){
tree.init();
mp.clear();
}
void solve(){
// init();
n=read(),q=read();
sum[0]=0;
rp(i,1,n){
a[i]=read();
sum[i]=sum[i-1]^a[i];
pre[i]=mp[a[i]];
mp[a[i]]=i;
}
rp(i,1,q) p[i].l=read(),p[i].r=read(),p[i].pos=i;
sort(p+1,p+1+q,[&](node a,node b){
return a.r<b.r;
});
int now=1;
rp(i,1,q){
int l=p[i].l,r=p[i].r;
while(now<=r){
if(pre[now]) tree.insert(pre[now],a[now]);
tree.insert(now,a[now]);
now++;
}
ans[p[i].pos]=sum[r]^sum[l-1]^tree.query(r)^tree.query(l-1);
}
rp(i,1,q) printf("%lld\n",ans[i]);
}
/*
预处理维护出区间异或和(出现奇数次)
然后对数组进行离散化
再用树状数组维护出区间异或和(所有数)
*/
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=1;
while(T--) solve();
/*
if(debug) {
end = clock();
printf("time:%.2fs\n", 1.0 * (end - beg) / CLOCKS_PER_SEC);
}
*/
return 0;
}
比赛过程
比赛开始时我因为有事耽误了一会,匆匆开局等到队友把 \(A\) 题 \(A\) 了才发现自己看错题了。
换 \(C\) 后推了个解法,一发 \(A\) 。 \(E\) 题推了个解法也顺利 \(A\) 掉。队友也 \(A\) 了 \(A,B,G,L\) 等题。
之后我和 \(wjh\) 开始看 \(F\) 题, \(mjh\) 在看 \(H\) ,等我们 \(F\) 题一直 \(T\) ,卡自闭了之后就想换题了,奈何剩下一个能开的是个博弈相关的,看了一下也没思路。 \(mjh\) 卡了之后也转战博弈,有一些思路但实现起来很难。全体陷入自闭。
\(mjh\) 又换回 \(H\) 题,没多久有了新思路,顺利 \(A\) 掉。博弈题我猜了个结论,写了个贪心,结果 \(WA2\) 了,然后比赛结束。
总结
zwt
\(CF\) 的评测姬跑 \(1e6\) 的 \(O(nlogn)\) 是不会炸的~
mjh
比赛前期没有快速进入状态导致签到过的有点慢,中期时看到了一道反演题,然后就被勾住了魂,想了大概一个多小时,所幸最后想到了解法,但是因为没有笔导致推公式有点慢,所幸最后推出来了,不过没有时间去看\(F\)题了,当时的决策应该是和队友\(debugF\)题,然后再去开其他题,当最后没有好的题可做时再选择反演,反演的题也好长时间没有做了,需要重新刷一遍了。