2020 年第一届辽宁省大学生程序设计竞赛——全部题目

oj: 牛客

A.组队分配(签到题)

oj: 牛客

题解

排序后直接输出。

代码

#pragma GCC optimize(2)
#include 
#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 
#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
#define pll pair
#define pil pair
#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 
#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聚聚的思路
2020 年第一届辽宁省大学生程序设计竞赛——全部题目_第1张图片
2020 年第一届辽宁省大学生程序设计竞赛——全部题目_第2张图片

代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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
#define pll pair
#define pil pair
#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=lraij=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=lraij=i+1raj2i=lraij=lraji=lrai2
因此我们只需要维护一下区间平方和+区间和就行了。
直接魔改一下hdu4578的模板代码
最后的时候注意一下负数的情况。

代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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
#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<
		}
		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<
			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 2m2t

我们在出现次数为1的串里找一个这样的串: 串内出现次数为奇数的字符 的个数不超过1。这样我们就能把这个串作为回文中心。对答案的贡献是 m m m

代码

#pragma GCC optimize(2)
#include 
#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 
#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 
#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
#define pll pair
#define pil pair
#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<
	if(n1==0){
		string tt="";
		double ans=0.5;
		while(n2>0){
			if(n2>ans||fabs(n2-ans)<=eps){
				// cout<
				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<
	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<
	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 
#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
#define pll pair
#define pil pair
#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 
#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
#define pll pair
#define pil pair
#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分为一组,两边的23就无处安放了。正确的分法应该是2 32 3

那应该怎么分呢?

考虑将 a a a 从左到右贪心地分成若干个足够小的异或和为x的组,数组内每一个元素都在一个这样的组内。例如当 x x x 1 1 1 时, a a a1 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] 一样了,没必要重复计算。

边界:

  1. 考虑 b b b 数组最后一个 u x ux ux,答案时 1 1 1 d p [ − 1 ] = 1 dp[-1] = 1 dp[1]=1(-1代表最后一个元素,后面以此类推)。
  2. 考虑 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 种。

转移:

  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]
  2. 其二是将 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 
#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题。

你可能感兴趣的:(组队赛,程序设计,算法,acm竞赛)