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\)题,然后再去开其他题,当最后没有好的题可做时再选择反演,反演的题也好长时间没有做了,需要重新刷一遍了。

posted @ 2021-02-01 18:57  指尖跳动的电光  阅读(193)  评论(0编辑  收藏  举报