自己高一和高二考省选时抱有截然不同的心态,高一的时候只是想着去玩,甚至都没有好好复习。有时候想如果自己还是高一就好了,就还有机会,也不至于没有退路,这么狼狈。
高一的学弟好厉害啊,现在想来自己高一真实地划了一年水…有点自惭形秽
我已经做好了面对各种结果的心理准备了,自己总是能调整好心态的
祝自己以及各位好运吧
到考点发现大家都已经进去了,赶紧跟上去
进了考场,发现键盘居然没有坏掉的键,就是有点粘。
开考前惊讶地发现系统时间是错的,真坑。。调了下时间,然后把配置打了
怎么只有一个PDF???我拿错包了?
今年连大样例都没了?差评
一看题目感觉画风好像不太对,怎么又有鱼,又有多边形的?
题目描述得似乎有点不太清楚。T2的字符集大小甚至都不说,差评。
下午仔细想想好像被题意杀了?有点懵逼,产生了一种考ZJOI的错觉
安姥爷好像翻车了,可能心态崩了
来得稍早一些。盘都不知道是什么…心情比较复杂
打开包,果然没有大样例,感觉自己已经无所畏惧了= =
盯了好久T2都没有想法,k甚至没有部分分范围。心态比较崩,一直在往dp快速幂方面上想却没有进展。本来看到数据范围明示了单位根反演,但是余数又不为0,因此放弃了单位根反演,出了考场听说是 [ k ∣ i − t ] [k|i-t] [k∣i−t] 再单位根反演
仔细想想,当时时间有些不太够了,心情也比较紧张,T2也就一直没有去尝试。下考前手忙脚乱,差点把T1源程序给删了。。
下午心情比较煎熬,一直等到很晚才回去,然而既没有申诉也没有看到成绩…
感觉今年的题目没什么区分度,联赛翻车选手恐怕没有翻盘机会了
D1T3只写了傻逼暴搜。暴力分都没拿到,后来拿到题面,仔细一想,只用了不到30min就会做65分了。。然而我Day1却认为它是不可做题,根据做题策略,把它的暴力也留到了最后一个小时才打,如果有这么45分,我们学校今年就能进5个人,也不至于被联赛拖了后腿
其次还因为一些原因有两道题爆了零。。我觉得我可能应该感谢sequence的出题人,50分居然算高分,把我从退役的边缘上拉了上来
有点自闭…不对,很自闭
又重新翻了翻自己考前奶的知识点,发现奶中了myy会出题以及一道字符串题,还奶中了一道冷门算法不可做题= =
还在填坑 (可能不知道什么时候就弃坑了)
不妨用 d i d_i di 表示所有三角剖分边中和 i i i 点相连的边数。
已经和 n n n 相连的边无法再被旋转,那么最终局面肯定是所有点都和 n n n 连边。观察样例解释+画图可以得到结论,每次操作都必然可以使得一条边旋转成与 n n n 相连的边。那么第一问的答案就是 n − 3 − d n n-3-d_n n−3−dn。
再来考虑第二问,不难发现有一些限制,即旋转某条边之前必须先旋转某条边。这些边构成了一个树形结构,且是一个二叉树森林,可以用一个虚根把森林连起来。那么第二问其实就是问这个森林的拓扑序个数,用HNOI2015实验比较的方法去做即可拿到65分。
这里我们考虑子树 u u u 的答案
a n s u = ( s z u − 1 ) ! ∏ v a n s v s z v ! ans_u=(sz_u-1)!\prod_v\frac {ans_v} {sz_v !} ansu=(szu−1)!v∏szv!ansv
单独考虑一下每个点,除了根节点外,每个点仅在自己和其父亲上算了贡献,贡献为 1 s z x \frac 1 {sz_x} szx1。最后单独考虑根的贡献,就是 ( s z r t − 1 ) ! = ( n − 3 − d n ) ! (sz_{rt}-1)!=(n-3-d_n)! (szrt−1)!=(n−3−dn)!。子树大小其实可以快速计算,而不需要建立树结构。一条边 ( l , r ) (l,r) (l,r) 分割出编号在 [ l , r ] [l,r] [l,r] 中的点的多边形,所有在这个多边形内的三角剖分边都在它的子树内,因此其子树大小即为 r − l − 1 r-l-1 r−l−1。
由此我们单独计算每条边的贡献即可,模拟旋转操作时用set找前驱后继即可找到新加入的边。
时间复杂度 O ( ( n + m ) log n ) O((n+m)\log n) O((n+m)logn)。
#include
#include
using namespace std;
typedef long long ll;
const int maxn=100010,mod=1e9+7;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
int W,n,m,ans=1,d[maxn],fac[maxn],inv[maxn];
set<int> G[maxn];
set<int>::iterator itl,itr;
int abs(int x){return x<0?-x:x;}
int power(int x,int y)
{
int res=1;
for(;y;y>>=1,x=(ll)x*x%mod)
if(y&1)
res=(ll)res*x%mod;
return res;
}
void add(int x,int y)
{
++d[x];++d[y];
if(x^n&&y^n) ans=(ll)ans*(abs(x-y)-1)%mod;
}
void del(int x,int y)
{
--d[x];--d[y];
if(x^n&&y^n) ans=(ll)ans*power(abs(x-y)-1,mod-2)%mod;
}
void work()
{
printf("%d",n-d[n]-3);
if(W) printf(" %lld",(ll)fac[n-d[n]-3]*power(ans,mod-2)%mod);
putchar('\n');
}
int main()
{
int x,y,nx,ny;
read(W);read(n);
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=(ll)fac[i-1]*i%mod;
inv[n]=power(fac[n],mod-2);
for(int i=n-1;~i;i--) inv[i]=(ll)inv[i+1]*(i+1)%mod;
for(int i=1;i<=n-3;i++)
{
read(x);read(y);add(x,y);
G[x].insert(y);G[y].insert(x);
}
G[1].insert(n);G[n].insert(1);
for(int i=1;i<n;i++) G[i].insert(i+1),G[i+1].insert(i);
work();
read(m);
while(m--)
{
read(x);read(y);
itl=itr=G[x].find(y);
--itl;++itr;
nx=*itl;ny=*itr;
del(x,y);
add(nx,ny);
work();
del(nx,ny);
add(x,y);
}
return 0;
}
首先你要会做30分。。可以考虑如果现在有一个回文串,只需要在头部和尾部增加一个相同的字符就可以得到另一个合法的回文串了。那么从奇数和偶数的分别枚举边,记忆化一下即可做到 O ( m 2 + Q ) O(m^2+Q) O(m2+Q)
瓶颈在于枚举边,那么可以考虑减少边的规模。不妨先考虑所有数字相同的联通块,注意到反复走某条边可以得到相同奇偶性的任意长度的路径,那么我们就只需要知道联通块内两点间路径奇偶性即可。可以只保留一棵生成树,当联通块不是二分图时就可以任意改变奇偶性,那么加一个自环即可。YY一下,发现01之间的边也是同理的,而且连自环都不需要加了。那么边数减少到了 O ( 2 n ) O(2n) O(2n) 的级别。
再暴力dp即可做到 O ( n 2 + Q ) O(n^2+Q) O(n2+Q) 。
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=5010,maxm=500010;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
struct data{int v,nxt;}edge[maxn<<2];
struct node{int x,y;};
int n,m,Q,p,a[maxn],head[maxn];
char s[maxn];
bool ans[maxn][maxn];
queue<node> q;
int CNT=0;
void insert(int u,int v)
{
edge[++p]=(data){v,head[u]};head[u]=p;
edge[++p]=(data){u,head[v]};head[v]=p;
++CNT;
if(u^v&&a[u]==a[v]){ans[u][v]=ans[v][u]=1;q.push((node){u,v});}
}
namespace Graph{
int t,p,head[maxn],cor[maxn];
data edge[maxm<<1];
void ins(int u,int v)
{
edge[++p]=(data){v,head[u]};head[u]=p;
edge[++p]=(data){u,head[v]};head[v]=p;
}
void dfs(int x,int f)
{
for(int i=head[x];i;i=edge[i].nxt)
if((a[x]^a[edge[i].v])==f)
{
if(cor[edge[i].v]==-1)
{
cor[edge[i].v]=cor[x]^1;
insert(x,edge[i].v);
dfs(edge[i].v,f);
}
if(cor[x]==cor[edge[i].v]) t|=1;
}
}
void build()
{
memset(cor,0xff,sizeof(cor));
for(int i=1;i<=n;i++)
if(cor[i]==-1)
{
t=cor[i]=0;
dfs(i,0);
if(t) insert(i,i);
}
memset(cor,0xff,sizeof(cor));
for(int i=1;i<=n;i++)
if(cor[i]==-1)
{
cor[i]=0;
dfs(i,1);
}
}
}
using Graph::ins;
void input()
{
int u,v;
read(n);read(m);read(Q);
scanf("%s",s+1);
for(int i=1;i<=n;i++){a[i]=s[i]-'0';ans[i][i]=1;q.push((node){i,i});}
for(int i=1;i<=m;i++)
{
read(u);read(v);
ins(u,v);
}
Graph::build();
}
void work()
{
int x,y,nx,ny;
while(!q.empty())
{
x=q.front().x;y=q.front().y;q.pop();
for(int i=head[x];i;i=edge[i].nxt)
for(int j=head[y];j;j=edge[j].nxt)
if(a[edge[i].v]==a[edge[j].v])
{
nx=edge[i].v;ny=edge[j].v;
if(ans[nx][ny]) continue;
ans[nx][ny]=ans[ny][nx]=1;
q.push((node){nx,ny});
}
}
while(Q--)
{
read(x);read(y);
puts(ans[x][y]?"YES":"NO");
}
}
int main()
{
input();
work();
return 0;
}