CSP-S2019游记
今年\(CSP\)
比去年难太多了,我也\(AFO\)了
去年\(2018NOIp\)没有写游记
今年还是写点吧
华丽的分割线
考前我疯狂的复习了图论
像 割点啊,缩点啊,网络流,生成树之类的
然后 一个都没考到 QAQ
\(Day1\)
\(T1\) 格雷码
\(emm\) 这题不难 但我想了老久了
心态差点就崩了 也耗了很多时间
但是我们考场 发生了点特殊情况
于是延迟了一个半小时 \(lucky\)
然后想出来了
递归处理
但是测试了极限数据
\(n = 64\)时 崩了
我就慌得一批
做完\(T2\)后 才发现
ULL num = qkpow(2,n) / 2;
改成
ULL num = qkpow(2,n - 1);
极限数据过了!!!
后来交洛谷 却\(WA\)了一点 很迷
不过 那是后来的事了
以下均隐藏\(freopen\)
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <iostream>
using namespace std;
#define isdigit(x) ('0' <= x&&x <= '9')
template<typename T>
inline T Read(T Type)
{
T x = 0,f = 1;
char a = getchar();
while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
return x * f;
}
typedef unsigned long long ULL;
inline ULL qkpow(ULL a,int n)
{
ULL res = 1;
while(n)
{
if(n & 1) res *= a;
a *= a;
n >>= 1;
}
return res;
}
inline void dfs(ULL num,ULL k,char f)
{
if(num == 0ull) return;
if(f == '0')
{
if(k <= num) putchar('0'),dfs(num / 2,k,'0');
else putchar('1'),dfs(num / 2,k - num,'1');
} else {
if(k <= num) putchar('1'),dfs(num / 2,k,'0');
else putchar('0'),dfs(num / 2,k - num,'1');
}
}
int main()
{
int n = Read(1);ULL k = Read(1ull) + 1ull;
ULL num = qkpow(2,n - 1);
if(k <= num) putchar('0'),dfs(num / 2,k,'0');
else putchar('1'),dfs(num / 2,k - num,'1');
return 0;
}
\(T2\) 括号树
我以为是\(dp\)然后没想出来
就打了暴力QAQ
最开始我被
互不相同的子串
给坑了
那 \(((())()\)
\(()\) 和 \(()\)合起来只算一次
得拿个\(map\)记录一下 那不就\(n^3\)了吗
???
然后我看见了
子串视作不同当且仅当它们在 S 中的位置不同,即 l 不同或 r 不同。
哎 关于考场上\(30min\)是怎么浪费的
华丽的分割线
我的思路是
当 遇到
'('时
一定有\(ans_i=ans_{fa}\)
')'
一定有\(ans_i=ans_{fa}+need_{fa}\)
\(need_i\)记录从\(root\)到\(i\)还需要一个')'成为合法括号串的个数
那么\(need_i\)还需要用从\(root\)到\(i\)还需要两个')'成为合法括号串的个数
以此类推 直接\(n^2\)
然后仔细想想
把数组整体往后移一位
匹配好的不要
新生成的差一个成为合法括号串
这些操作都可以用 \(vector\)来啊
\(insert,erase\)
然后时间复杂度为\(O(n*insert)\)
由于不知道\(insert\)一次是多少
所以复杂度很玄
但洛谷\(A\)了
现在能做的便只有祈祷了啊
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <climits>
#include <cstring>
#include <iostream>
using namespace std;
#define isdigit(x) ('0' <= x&&x <= '9')
template<typename T>
inline T Read(T Type)
{
T x = 0,f = 1;
char a = getchar();
while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
while(isdigit(a)) {x = (x << 1) + (x << 3) + (a ^ '0');a = getchar();}
return x * f;
}
#define reg register int
typedef long long ll;
const int MAXN = 5e5 + 10;
struct point
{
char rac;
int fa,dp;
ll ans;
}f[MAXN];
int cnt,_ori[MAXN];
vector<int> nd;
struct EDGE
{
int v,u,_nxt;
}edge[MAXN];
inline void add(int u,int v)
{
edge[++cnt].v = v;
edge[cnt].u = u;
edge[cnt]._nxt = _ori[u];
_ori[u] = cnt;
}
bool vis[MAXN];
inline void dfs(int x,int fa)
{
vis[x] = 1;
for(reg e = _ori[x],v;e;e = edge[e]._nxt)
{
if((v = edge[e].v) == fa||vis[v]) continue;
f[v].ans = f[x].ans;
int able = -1;
if(f[v].rac == ')')
{
if(nd.size() > 0)
{
f[v].ans += (able = nd[0]);
f[v].dp = nd[0];
nd.erase(nd.begin());
}
} else {
f[v].dp = 0;
nd.insert(nd.begin(),1 + f[x].dp);
}
dfs(v,x);
if(f[v].rac == ')')
{
if(able != -1) nd.insert(nd.begin(),able);
} else nd.erase(nd.begin());
}
}
int main()
{
// freopen("brackets.in","r",stdin);
// freopen("brackets.out","w",stdout);
memset(f,0,sizeof(f));
int n = Read(1);
for(reg i = 1;i <= n;i++)
{
char a = getchar();
while(a != '('&&a != ')') a = getchar();
f[i].rac = a;
}
for(reg i = 2;i <= n;i++)
{
int v = Read(1);
f[i].fa = v,add(v,i);
}
f[1].fa = 0;
if(f[1].rac != ')') nd.push_back(1);
dfs(1,0);
ll ans = f[1].ans;
for(reg i = 2;i <= n;i++) ans ^= (f[i].ans * i);
cout << ans;
return 0;
}
\(T3\) 树上的数
不会
好难
完全无思路
死了
弃疗了
于是
这是我的代码
#include <cstdio>
using namespace std;
#define reg register int
int main()
{
printf("1 3 4 2 5\n"
"1 3 5 2 4\n"
"2 3 1 4 5\n"
"2 3 4 5 6 1 7 8 9 10");
return 0;
}
\(Day2\)
\(T1\) \(Emiya\)家今天的饭
其实我看到\(Emiya\)就想到 \(Fate/Stay Night [Unlimited Blade Works]\)去了QAQ
感觉像是 \(DP\)
没想出来
到现在都还不会做
当时做题时一直在想 \(T1\) 不至于这么难
也许 只有我没做出来吧 QAQ
考场上差点心态崩了
想了大概 \(2h+\)吧
先想了一个小时 然后就去做\(T2,T3\)
最后一个小时来打了暴力
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
#define reg register int
#define isdigit(x) ('0' <= x&&x <= '9')
template<typename T>
inline T Read(T Type)
{
T x = 0,f = 1;
char a = getchar();
while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
while(isdigit(a)) x = (x << 1) + (x << 3) + (a ^ '0'),a = getchar();
return x * f;
}
typedef long long ll;
const ll mod = 998244353;
const int MAXN = 110,MAXM = 2e3 + 10;
int a[MAXN][MAXM],b[MAXM],n,m;
ll ans,res;
inline void dfs(int x,int k,int cnt)
{
if(k > n / 2||k > (cnt + n - x + 1) / 2) return;
if(x == n + 1)
{
if(k <= cnt / 2)
ans = (ans + res) % mod;
return;
}
dfs(x + 1,k,cnt);
for(int j = 1;j <= m;j++)
{
if(a[x][j])
{
ll past_res = res;
res = (res * a[x][j]) % mod;
b[j]++;
dfs(x + 1,max(k,b[j]),cnt + 1);
res = past_res;
b[j]--;
}
}
}
int main()
{
n = Read(1),m = Read(1);
for(reg i = 1;i <= n;i++)
for(reg j = 1;j <= m;j++)
a[i][j] = Read(1);
res = 1;
dfs(1,0,0);
printf("%lld",ans - 1);
return 0;
}
\(T2\) 划分
感觉以前做过的 但留给这道题的时间真不多了(\(T1\)还没写 \(T3\)暴力还没写)
于是我审完题后就匆匆的写了个看\(Rp\)的\(code\)
大致思想就是直接枚举起点 然后每遍暴力
取个最小值
算法不能保证正确性
感觉可以骗点分就打了
最后洛谷上只骗到了\(2\)个点(\(8pts\) 希望官方数据可以骗更多 \(RP++\))
#include <cstdio>
#include <iostream>
using namespace std;
#define reg register int
#define isdigit(x) ('0' <= x&&x <= '9')
template<typename T>
inline T Read(T Type)
{
T x = 0,f = 1;
char a = getchar();
while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
while(isdigit(a)) x = (x << 1) + (x << 3) + (a ^ '0'),a = getchar();
return x * f;
}
typedef long long ll;
const int MAXN = 5e5 + 10;
int n;
ll a[MAXN];
inline ll count_ans(ll pow_)
{
ll ans = 0,res = 0,maxl = pow_;
for(reg i = 1;i <= n;i++)
{
res += a[i];
if(res >= maxl) ans += res * res,maxl = max(res,maxl),res = 0;
}
if(res == 0) return ans;
return 0;
}
ll sum;
int main()
{
n = Read(1);Read(1);
for(reg i = 1;i <= n;i++) a[i] = Read(1ll);
ll ans = 0,prans;
for(reg i = 1;i <= n;i++)
{
sum += a[i];
ans = count_ans(sum);
if(!ans) break;
if(i == 1) prans = ans;
else prans = min(ans,prans);
}
printf("%lld",prans);
return 0;
}
\(T3\) 树的重心
看到这道题
第一个想法是\(2\)次 \(dfs+DP\) 然后考虑换根
(最近做了几道换根题然后满脑子的换根)
然后 就没想出来
老老实实地打的暴力
一看数据范围 可以过前八个数据点
然后
\(A\)特殊数据 特殊骗分
完美二叉树的骗分没想出来 (我真是太菜了)
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define reg register int
#define isdigit(x) ('0' <= x&&x <= '9')
template<typename T>
inline T Read(T Type)
{
T x = 0,f = 1;
char a = getchar();
while(!isdigit(a)) {if(a == '-') f = -1;a = getchar();}
while(isdigit(a)) x = (x << 1) + (x << 3) + (a ^ '0'),a = getchar();
return x * f;
}
typedef long long ll;
const int MAXN = 3e5,MAXM = 3e5;
int cnt,_ori[MAXN];
struct EDGE
{
int u,v,_nxt;
}edge[MAXM << 1];
inline void add(int u,int v)
{
edge[++cnt].v = v;
edge[cnt].u = u;
edge[cnt]._nxt = _ori[u];
_ori[u] = cnt;
}
int _size[MAXN];
inline void dfs(int x,int fa)
{
_size[x] = 1;
for(reg e = _ori[x],v;e;e = edge[e]._nxt)
{
if((v = edge[e].v) == fa) continue;
dfs(v,x);
_size[x] += _size[v];
}
}
int rt,son[MAXN];
inline int dfs2(int x,int fa,int num)
{
bool able = 1;
int res = 0;
for(reg e = _ori[x],v;e;e = edge[e]._nxt)
{
if((v = edge[e].v) == fa) continue;
int ans = dfs2(v,x,num);
res += ans - num;
if(_size[v] > _size[rt] / 2) able = 0;
}
if(_size[rt] - _size[x] > _size[rt] / 2) able = 0;
return num + res + (able?x:0);
}
int a[MAXN],tot;
inline void Dfs(int x,int fa)
{
a[++tot] = x;
for(reg e = _ori[x],v;e;e = edge[e]._nxt)
{
if((v = edge[e].v) == fa) continue;
Dfs(v,x);
}
}
int main()
{
int T = Read(1);
while(T--)
{
int n = Read(1);
memset(_ori,0,sizeof(_ori));
memset(son,0,sizeof(son));
cnt = 0;
for(reg i = 1;i < n;i++)
{
int u = Read(1),v = Read(1);
add(u,v),add(v,u),son[u]++,son[v]++;
}
bool able = 0;
for(reg i = 1;i <= n;i++)
{
if(son[i] != 2&&son[i] != 1)
{
able = 1;
break;
}
if(son[i] == 1) rt = i;
}
ll ans = 0;
if(!able)
{
tot = 0;
memset(a,0,sizeof(a));
Dfs(rt,0);
for(reg i = 1;i < n;i++)
{
int len1 = i,len2 = n - i;
if(len1 & 1) ans += a[(len1 + 1) / 2];
else ans += a[len1 / 2] + a[len1 / 2 + 1];
if(len2 & 1) ans += a[len1 + (len2 + 1) / 2];
else ans += a[len1 + len2 / 2] + a[len1 + len2 / 2 + 1];
}
printf("%lld\n",ans);
} else {
for(reg i = 1;i <= cnt;i += 2)
{
memset(_size,0,sizeof(_size));
dfs(edge[i].u,edge[i].v),rt = edge[i].u;
ans += dfs2(edge[i].u,edge[i].v,0);
dfs(edge[i].v,edge[i].u),rt = edge[i].v;
ans += dfs2(edge[i].v,edge[i].u,0);
}
printf("%lld\n",ans);
}
}
return 0;
}
最后的最后
总分大概\(280\)吧
省一不用想了
省二没用 而且我已经有一张了
这个成绩大概十分不好吧
但是我尽了全力
再来一次 像\(JX\) 我大概也是这个分数吧
所以啊 我说过的 省一就是我梦想的极限了
我现在高一了
明年高二
高三不可能再搞了
明年就是最后的机会了
然后 我就该退役了(\(AFO\))了
这次实力不足
我能做的大概只有从现在开始为明年的作准备吧
因为啊
如果 最后我真的 没有拿到奖
那我这两年(明年就第三年了) 也就真的白费了
我不想让所有的心血付诸东流
不想让所有的艰难的日子里都挺过来的我失望
不想白费自己的努力
以及 作为我学过 当过\(Oier\)的证明
我必须在明年拿到省一了
——以此 纪念我爆炸的CSP-s2019