2020 年第一届辽宁省大学生程序设计竞赛(所有题目)

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\) 取模,否则会出现负数。

代码

#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]\)数字的乘积和。
转换为数学公式为\(\sum_{i=l}^{r}a_{i}\sum_{j=i+1}^{r}a_{j}\)
我们对公式进行推导可以得出

\[\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} \]

因此我们只需要维护一下区间平方和+区间和就行了。
直接魔改一下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,那么这个串就一定能作为回文串的两端中的某一段,并且出现偶数次。对答案的贡献是 \(2m\lfloor \frac{t}{2}\rfloor\)

我们在出现次数为1的串里找一个这样的串: 串内出现次数为奇数的字符 的个数不超过1。这样我们就能把这个串作为回文中心。对答案的贡献是 \(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\),所以我们筛出 \(15000\) 以内的素数就足够了。

先判断 \(n\) 是不是素数,这一步可以合并到寻找不小于 \(n\) 的最小的素数中。

分别找出不小于 \(n\) 的最小的素数和比 \(n\) 小的最大的素数。

不小于 \(n\) 的最小的素数是 *lower_bound(arr.begin(), arr.end(), d)。判断值是否与 \(n\) 相等。

\(n\) 小的最大的素数是 *upper_bound(arr.begin(), arr.end(), d)

如果 \(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.鸽子的浮点运算(模拟)

oj: 牛客

代码

#include <bits/stdc++.h>
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;
}

struct poi {
    int s;
    vector<int> e, m;
    poi(){}
    poi(double x) {
        e.clear(); m.clear();
        s = (x <= 0); x = fabs(x);
        int cnt = 15;
        while(x >= 2) {
            ++cnt;
            x /= 2;
        }
        while(x < 1) {
            --cnt;
            x *= 2;
        }
        for(int i = 0; i < 5; ++i) {
            e.push_back(cnt & 1);
            cnt >>= 1;
        }
        reverse(e.begin(), e.end());
        x -= (int)x;
        for(int i = 0; i < 10; ++i) {
            x *= 2;
            m.push_back((int)x);
            x -= (int)x;
        }
    }
    double getval() {
        double ans = 1, base = 1;
        for(int i = 0; i < 10; ++i) {
            base /= 2;
            if(m[i]) ans += base;
        }
        int q = 0, bas = 1;
        for(int i = 4; i >= 0; --i) {
            if(e[i]) q += bas;
            bas <<= 1;
        }
        q -= 15;
        while(q > 0) ans *= 2, --q;
        while(q < 0) ans /= 2, ++q;
        return ans * (s ? -1 : 1);
    }
    void print() {
        printf("%d", s);
        for(int i : e) printf("%d", i);
        for(int i : m) printf("%d", i);
        printf("\n");
    }
};

int main() {
    int T = read();
    for(int i = 0; i < T; ++i) {
        int op = read(); double x1, x2;
        scanf("%lf%lf", &x1, &x2);
        poi p1 = poi(x1), p2 = poi(x2);
        x1 = p1.getval(), x2 = p2.getval();
        double ans = x1;
        if(op == 2) ans = x1 + x2;
        else if(op == 3) ans = x1 * x2;
        poi pa = poi(ans);
        pa.print();
    }
    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\) 分成若干彼此互不相交的组,每组的异或和为 \(x\)。求分组的划分数。

思考一个问题:任何异或和为 \(x\) 的区间都能自成一组吗?

显然不是,例如当 \(x\)\(1\) 时的2 3 2 3中的3 2,虽然异或和为 \(1\),但如果把3 2分为一组,两边的23就无处安放了。正确的分法应该是2 32 3

那应该怎么分呢?

考虑将 \(a\) 从左到右贪心地分成若干个足够小的异或和为x的组,数组内每一个元素都在一个这样的组内。例如当 \(x\)\(1\) 时,\(a\)1 2 2 1 2 2 1时,我们将数组分为1, 2 2 1, 2 2 1

又发现分出的组还可以再分出一些足够小的异或和为0的组,将 \(a\) 进一步划分为:1, 2 2, 1, 2 2, 1。再例如1 2 2 2 2 1分为1, 2 2, 2 2, 1。之所以划分出异或和为 \(0\) 的组,是因为当他们出现在两个异或和为 \(1\) 的组之间时,它们既可以合并到左边的组,也可以合并到右边的组,所以他们的数量值得我们关注。

为了方便下文叙述,我们将 异或和为 \(x\) 的组 称为 \(ux\),将 异或和为 \(0\) 的组 称为 \(u0\),将 把数组 \(a\) 分成若干彼此互不相交的组且每组的异或和为 \(x\) 的划分数 称为 划分数

这样分完之后,所有的合理的对数组 \(a\) 的划分都是 \(ux\)\(u0\) 组合的结果。而且每次必须选奇数个 \(ux\) 合并到一起,不然每个 \(ux\) 的值为 \(x\),偶数个 \(x\) 异或之后为 \(0\),而不是 \(x\)

为了方便进行状态转移,我们在 \(ux\) 中间插入 \(u0\) 的数量,如果不存在 \(u0\) 就插入 \(0\)。我们把这个数组叫做数组 \(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。对于数组两端的 \(u0\),我们是不考虑的。因为它们只能被合并到相邻的 \(ux\) 而不能单独存在,对答案没有贡献,所以就当它们没有出现过就行了。

然后定义 \(dp[i]\)\(b[i:]\)(从 \(i\) 到末尾)的划分数。且只考虑 \(i\) 为偶数的情况( \(i\)\(0\) 开始计数)之所以不考虑 \(i\) 为奇数的情况是因为奇数位置对应的都是 \(u0\) 的个数,上面也说了以 \(u0\) 作为数组边界时时可以将其视作不存在,所以 \(dp[偶数]\) 就和 \(dp[偶数+1]\) 一样了,没必要重复计算。

边界:

  1. 考虑 \(b\) 数组最后一个 \(ux\),答案时 \(1\)\(dp[-1] = 1\)(-1代表最后一个元素,后面以此类推)。
  2. 考虑 \(b\) 数组最后两个 \(ux\),不能执行合并操作。中间有 \(dp[-2]\)\(u0\),可以划分 \(dp[-2]\)\(u0\) 到左边,其他的划分到右边,也可以划分 \(dp[-2]-1\) 个到左边,也可以划分 \(dp[-2]-2\) 个...,划分法就有 \(dp[-2]+1\) 种。

转移:

  1. \(i\) 个状态有两种转移方式,其一是将 \(b[i]\)\(b[i+2]\) 合并,但是由于每次合并必须是奇数个 \(ux\),所以将 \(b[i]\) 合并到 \(b[i+2]\) 后的组合数实际上是由 \(b[i+4]\) 决定的。可以认为把 \(b[i]\), \(b[i+2]\), \(b[i+4]\) 绑定到了一起(中间的 \(u0\) 也是绑在一起),这样就可以把他们仨看成只有一个 \(b[i+4]\)。所以此时 \(dp[i]+=dp[i+4]\)
  2. 其二是将 \(b[i]\) 单独作为一个组(可能前面会有别的组选择和 \(b[i]\) 合在一起,但此时不考虑前面的),这时 \(b[i]\)\(b[i+2]\) 中间有 \(b[i+1]\)\(u0\),一共有 \(b[i+1]+1\) 种划分方式(参考边界.2)。每种又对应 \(dp[i+2]\) 种,所以 \(dp[i] += (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题。

posted @ 2021-01-23 21:05  指尖跳动的电光  阅读(390)  评论(0编辑  收藏  举报