文章目录
比赛过程
1006有些考验思维,被卡了很久,这次又犯了三人同时开一道题的错误。其实1008这道模拟和1009的字符串都不算太难。
题解
1003
题意
给定在同一个圆上的三个点,求一次经过A B C三点的方向是顺时针还是逆时针。
解法
用矢量叉积求出A在B的左边还是右边,然后再特判一下C是否在A和B中间即可。
代码
int main()
{
IO;
int t;
cin >> t;
while (t--)
{
ll x1,y1,x2,y2,x3,y3;
cin>>x1>>y1>>x2>>y2>>x3>>y3;
ll ans1=x1*y2-x2*y1;
ll ans2=x2*y3-x3*y2;
ll ans3=x1*y3-x3*y1;
if(ans1>0)
{
if(ans2<0&&ans3>0)
cout<<"Clockwise\n";
else cout<<"Counterclockwise\n";
}
else
{
if(ans2>0&&ans3<0)
cout<<"Counterclockwise\n";
else cout<<"Clockwise\n";
}
}
}
1004(119/799) Discovery of Cycles
tag:LCT
题意
一张无向图,q个强制在线询问,如果只用区间[l,r]的边,能否组成一个简单环。在线询问。
官方题解
我们可以给每一条边 i 求出以这条边作为左端点, 在最短的区间内能形成环的最小右端点, 标记为 Ri, 如果不存在这样的右端点(即从当前到结尾所有边都不能组成环), 则让 Ri = m + 1 , 显然暴力求这个 Ri 是不允许的。
显然 Ri 一定是递增的, 所以如果我们可以快速删边的话, 那么这个就能快速的求解了.于是就考虑到可以用 Link-Cut-Tree 来优化这个过程, 即只需要维护左端点和右端点, 肯定会尽量拓展右端点, 拓展不了就删掉左端点所在的边然后移动左端点.不断重复这个过程就可以求解所有的 Ri了。时间复杂度 O(m log n + q).
int _, n, m, q, edge[N][2], maxn[N], Stack[N];
struct node {
int l, r, fa, pre;
bool flag;
}a[N];
void init() {
ms(a, 0), ms(edge, 0), ms(maxn, 0), ms(Stack, 0);
}
// 右旋
inline void zig(int x) {
int y = a[x].fa, z = a[y].fa;
if(y == a[z].l) a[z].l = x;
if(y == a[z].r) a[z].r = x;
a[x].fa = z, a[y].fa = x;
a[y].l = a[x].r, a[a[x].r].fa = y, a[x].r = y;
}
// 左旋
inline void zag(int x) {
int y = a[x].fa, z = a[y].fa;
if(y == a[z].l) a[z].l = x;
if(y == a[z].r) a[z].r = x;
a[x].fa = z, a[y].fa = x;
a[y].r = a[x].l, a[a[x].l].fa = y, a[x].l = y;
}
inline void push(int x) {
if(!a[x].flag) return;
a[a[x].l].flag ^= 1;
a[a[x].r].flag ^= 1;
swap(a[x].l, a[x].r);
a[x].flag = 0;
}
inline void Splay(int x) {
int rt = x;
for(; a[rt].fa; rt = a[rt].fa) Stack[++Stack[0]] = rt;
push(rt);
while(Stack[0]) push(Stack[Stack[0]--]);
if(rt == x) return;
a[x].pre = a[rt].pre;
a[rt].pre = 0;
while(a[x].fa) {
int y = a[x].fa, z = a[y].fa;
if(x == a[y].l) {
if(y == a[z].l) zig(y);
zig(x);
} else {
if(y == a[z].r) zag(y);
zag(x);
}
}
}
inline void expose(int x) {
for(int y = 0; x; x = a[x].pre) {
Splay(x);
a[a[x].r].fa = 0;
a[a[x].r].pre = x;
a[x].r = y;
a[y].fa = x;
a[y].pre = 0;
y = x;
}
}
inline bool connect(int u, int v) {
expose(u), Splay(u);
for(; a[v].fa || a[v].pre; v = a[v].fa ? a[v].fa : a[v].pre);
return u == v;
}
inline void make_root(int x) {
expose(x); Splay(x); a[x].flag ^= 1;
}
inline void add(int u, int v) {
make_root(u);
a[u].pre = v;
}
inline void cut(int u, int v) {
make_root(u), expose(v), Splay(v);
a[a[v].l].fa = 0; a[v].l = 0;
}
int main() {
for(cin >> _; _--; ) {
init();
scanf("%d%d%d", &n, &m, &q);
rep(i, 1, m) scanf("%d%d", &edge[i][0], &edge[i][1]);
// 预处理
for(int i = 1, t = 1; t <= m; ) {
if(i <= m && !connect(edge[i][0], edge[i][1])) {
add(edge[i][0], edge[i][1]);
i++;
} else {
maxn[t] = i;
cut(edge[t][0], edge[t][1]);
t++;
}
}
int last = 0;
while(q--) {
int u, v; scanf("%d%d", &u, &v);
u = (u ^ last) % m + 1;
v = (v ^ last) % m + 1;
if(u > v) swap(u, v);
if(v >= maxn[u]) last = 1, puts("Yes");
else last = 0, puts("No");
}
}
return 0;
}
1006
题意
给出n个区间,要求预测n个数,这n个数分别要在对应区间内,并且要求前后两数相差不超过k。判断能否预测这n个数。
解法
如果 i 是在 [l, r] 范围内, 那么 i + 1 必须要在 [l − k, r + k] 范围内.这是因为如果 i + 1 选了
范围外的值, i 就无解了.
这样可以从左往右, 把左边的约束带到右边.再从右往左做一遍.最后剩下的区间应该就是可
行域.因为题目只要求一种方案, 全部选最低的即可.复杂度 O(n).
其实这道题我们当时wa+T+MLE了11次,之后总结为什么会出现这种情况,问题一是没有想到正着来一次后还要反着来一次。问题二是这道题他的数据量真的卡的有点变态,我们起初用来vector里边套pair结果MLE,后来就开始T,T了好久之后发现我们之前都是对n个区间每输入一个区间就进行处理,处理完再输入下一个,这样就会T,但是如果改成先全部输入n个区间,再一一进行处理,这样就能刚好卡过,哭了哭了。
代码
struct pick{
ll first,second;
}P[maxn];
int main()
{
IO;
int t;
cin >> t;
while (t--)
{
bool flag = 0;
int n, k;
cin >> n >> k;
re(i,0,n-1)
cin>>P[i].first>>P[i].second;
re(i, 1, n - 1)
{
P[i].first = max(P[i].first, P[i-1].first-k);
P[i].second =min(P[i].second, P[i-1].second+k);
if (P[i].first> P[i].second)
{
flag = 1;
cout << "NO\n";
break;
}
}
if (flag)
continue;
de(i, n - 2, 0)
{
int lx = P[i + 1].first - k;
int rx = P[i + 1].second + k;
int ln = P[i].first;
int rn = P[i].second;
ln = max(ln, lx);
rn = min(rn, rx);
if (ln > rn)
{
flag = 1;
cout << "NO\n";
break;
}
P[i].first = ln, P[i].second = rn;
}
if (flag)
continue;
cout << "YES\n";
re(i, 0, n - 1)
cout
<< P[i].first << " ";
cout << endl;
}
}
1008
题意
模拟题找规律题,让你一笔画走遍所有的点,每个点只走一次,问你路径。
解法
把图画出来其实规律不算太难找,对于奇数,上图中第一个边的路径为4242423,第二个边的路径为5353534,会发现其实是全部加一的规律,到6的时候再加就会变成1,注意特判一下最后的边因为要往下一层走,所以最后一个数字不太一样。偶数和奇数只是最后剩下一层还是两层的问题,特判一下就行了。
代码
int main(){
//IO;
int t;
cin>>t;
while(t--){
int n;
cin >> n;
if(n%2==1){
for(int i=n;i>1;i-=2){
int a = 2, b = 3, c = 1;
FOR(j,1,6){
a = (a + 1) % 7;
b = (b + 1) % 7;
c = (c + 1) % 7;
if(a==0) a++;
if(b==0) b++;
if(c==0) c++;
if(j!=6){
cout << a;
FOR(k,1,i-2) cout << b << c;
}
else{
cout << a;
FOR(k,1,i-3) cout << b << c;
cout << b << 4;
}
}
}
}
else{
for(int i=n;i>2;i-=2){
int a = 2, b = 3, c = 1;
FOR(j,1,6){
a = (a + 1) % 7;
b = (b + 1) % 7;
c = (c + 1) % 7;
if(a==0) a++;
if(b==0) b++;
if(c==0) c++;
if(j!=6){
cout << a;
FOR(k,1,i-2) cout << b << c;
}
else{
cout << a;
FOR(k,1,i-3) cout << b << c;
cout << b << 4;
}
}
}
int a = 2, b = 3, c = 1;
FOR(j,1,6){
a = (a + 1) % 7;
b = (b + 1) % 7;
c = (c + 1) % 7;
if(a==0) a++;
if(b==0) b++;
if(c==0) c++;
if(j!=6){
cout << a;
}
else{
cout << 3;
}
}
}
cout << endl;
}
return 0;
}
1009
题意
给出一个串,判断咋个串是否是多个等长的循环同构串的拼接。
解法
哈希.
枚举 n 的约数 k, 那么你可以 O(n/k) 求出 s1, . . . , sk 的哈希值.同时你可以 O(k) 求出 s1 的循环同构串的哈希值.因此问题转化为, 给出两个数集 S, T, 判断是否有 S ⊆ T.这并不难, 把哈希模数设成 107 级别的, 开一个数组就可以 O(|S| + |T|) 判了.
总复杂度 O(σ(n)C).其中 σ(n) = ∑d|nd, C 为哈希模数个数.
这题其实不难,然而由于我们在1006上花了较多时间,就没有去开这道题,有点小遗憾。这道题的过程中我也T了,最后发现是用map不规范的锅,map这东西数据太大了还是少用的好,自动给你排序太费时间。
代码
const int maxn = 5e6 + 10;
const ll mode = 7000061;
char s[maxn];
ll f[maxn], p[maxn];
ll gethash(int l, int r)
{
ll ans = (f[r] - f[l - 1] * p[r - l + 1] % mode + mode) % mode;
return ans;
}
ll mp[maxn<<1];
ll cnt = 0, num[27];
bool check(int n, int len)
{
cnt++;
//求所有轮换的hash值
ll tem = gethash(1, len);
mp[tem] = cnt;
re(i, 1, len - 1)
{
tem = (tem - p[len - 1] * (ll)(s[i] - 'a' + 1) % mode + mode) % mode;
tem = tem * 29 % mode;
tem = (tem + (ll)(s[i] - 'a' + 1)) % mode;
mp[tem] = cnt;
}
//遍历子串
re(i, 1, n / len - 1)
{
ll ans = gethash(i * len + 1, (i + 1) * len);
if (mp[ans] != cnt)
return 0;
}
return 1;
}
int main()
{
int t;
si(t);
p[0] = 1;
re(i, 1, maxn - 3) p[i] = p[i - 1] * 29 % mode;
while (t--)
{
int n;
si(n);
ms(num, 0);
ss(s + 1);
re(i, 1, n)f[i] = (f[i - 1] * 29 % mode + (ll)(s[i] - 'a' + 1)) % mode;
ll mi = 1e8;
re(i,1,n)num[s[i]-'a'+1]++;
re(i, 1, 26)
{
if (num[i])
mi = min(mi, num[i]);
}
if (mi == 1)
{
pn;
continue;
}
bool ok = 0;
for (int j = 1; 1ll * j * j <= mi; j++)
{
if (mi % j == 0)
{
bool flag=0;
if(j>1&&n%j==0)
flag = check(n, n / j);
if (flag)
{
ok = 1;
break;
}
int fac = mi / j;
if (fac == j)
continue;
if (fac == 1)
continue;
if (n % fac)
continue;
flag = check(n, n/fac);
if (flag)
{
ok = 1;
break;
}
}
}
if (ok)
py;
else
pn;
}
return 0;
}