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}\)
我们对公式进行推导可以得出
因此我们只需要维护一下区间平方和+区间和就行了。
直接魔改一下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
分为一组,两边的2
和3
就无处安放了。正确的分法应该是2 3
和2 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]\) 一样了,没必要重复计算。
边界:
- 考虑 \(b\) 数组最后一个 \(ux\),答案时 \(1\)。\(dp[-1] = 1\)(-1代表最后一个元素,后面以此类推)。
- 考虑 \(b\) 数组最后两个 \(ux\),不能执行合并操作。中间有 \(dp[-2]\) 个 \(u0\),可以划分 \(dp[-2]\) 个 \(u0\) 到左边,其他的划分到右边,也可以划分 \(dp[-2]-1\) 个到左边,也可以划分 \(dp[-2]-2\) 个...,划分法就有 \(dp[-2]+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]\)。
- 其二是将 \(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题。