noip28
东方专场?
T1
%%%WYZG
话说我考场上还想二维hash来着
考虑只记录弹幕中x的相对位置。
先选定弹幕一个点作为基准点(第一个出现的x即可),然后,枚举其他的x,记录下坐标差,然后去方格图中枚举,每找到一个x就去用坐标差计算出其他可能的x坐标,然后判断计算出的位置上是否有x,如果有,就消掉,下回不再枚举,没有,就说明不符合题目要求,输出No。
记得特判没有x的情况。
代码一些小细节可能没考虑到,但数据水,能过。
Code
#include<cstdio>
#define MAX 1010
#define re register
namespace OMA
{
int t,n,m,a,b;
struct node
{
int x,y;
}pair[MAX*MAX];
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
signed main()
{
t = read();
while(t--)
{
bool flag1 = false;
char s1[MAX][MAX],s2[MAX][MAX];
n = read(),m = read(),a = read(),b = read();
for(re int i=1; i<=n; i++)
{
scanf("%s",s1[i]+1);
if(!flag1)
{
for(re int j=1; j<=m; j++)
{ if(s1[i][j]=='x'){ flag1 = true; break ; } }
}
}
bool flag2 = true;
int cnt = 0,x1 = 0,y1 = 0;
for(re int i=1; i<=a; i++)
{
scanf("%s",s2[i]+1);
if(flag2)
{
for(re int j=1; j<=b; j++)
{
if(s2[i][j]=='x')
{ x1 = i,y1 = j,flag2 = false; break ; }
}
}
}
if((!x1||!y1)&&flag1)
{ printf("No\n"); continue ; }
if((!x1||!y1)&&!flag1)
{ printf("Yes\n"); continue ; }
for(re int i=1; i<=a; i++)
{
for(re int j=1; j<=b; j++)
{
if(s2[i][j]=='x')
{
if(i==x1&&j==y1)
{ continue ; }
pair[++cnt] = (node){i-x1,j-y1};
}
}
}
flag1 = true;
for(re int i=1; i<=n; i++)
{
for(re int j=1; j<=m; j++)
{
if(s1[i][j]=='x')
{
s1[i][j]=='.';
for(re int k=1; k<=cnt; k++)
{
if(s1[i+pair[k].x][j+pair[k].y]!='x')
{ printf("No\n"); flag1 = false; break ; }
s1[i+pair[k].x][j+pair[k].y] = '.';
}
}
if(!flag1)
{ break ; }
}
if(!flag1)
{ break ; }
}
if(flag1)
{ printf("Yes\n"); }
}
return 0;
}
}
signed main()
{ return OMA::main(); }
T2
又又又读题死亡,奇数位置指的是位置的编号,不是坐标大小。
小球进洞模型,然而我都不知道有这玩意
考虑dp,设 \(dp_{n,i}\) 表示给定 \(n\)时 ,\(x_{i}-x_{i-1}\) 的贡献,转移时枚举第一次选了那个球,它往那边滚了,直接转移 \(O(n^{3})\)。
实际上我们并不需要枚举它往那边滚了,第一次选的球只有在 “i的旁边” 和 “不在i的旁边” 两种。所以复杂度 \(O(n^{2})\) 可过。
附80pts直接转移code
Code
#include<cstdio>
#define MAX 3100
#define re register
#define int long long
namespace OMA
{
int n,x[MAX<<1],ans;
int dp[MAX][MAX<<1];
const int p = 998244353;
inline int read()
{
int s=0,w=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*w;
}
inline int quickpow(int a,int b)
{
int ans = 1;
while(b)
{
if(b&1)
{ ans = ans*a%p; }
a = a*a%p;
b >>= 1;
}
return ans;
}
signed main()
{
n = read();
for(re int i=1; i<=n*2+1; i++)
{ x[i] = read(); }
for(re int i=1; i<=n; i++)
{
int inv1 = quickpow(i,p-2),inv2 = quickpow(i*2,p-2);
for(re int j=2; j<=2*i+1; j++)
{
int calc = (i*2-j+1)/2;
if(j&1)
{ (dp[i][j] += dp[i-1][j-2]%p*inv2%p) %= p; }
else
{ (dp[i][j] += dp[i-1][j]%p*inv2%p) %= p; }
(dp[i][j] += ((dp[i-1][j]*calc%p*inv1%p+dp[i-1][j-1]*inv2%p)%p+(i-calc-1)*dp[i-1][j-2]%p*inv1%p+inv2%p)) %= p;
/*for(re int k=1; k<=i; k++)
{
if(2*k>j)
{ (dp[i][j] += dp[i-1][j]*inv1%p) %= p; }
else if(2*k==j-1)
{ (dp[i][j] += dp[i-1][j-1]*inv2%p+dp[i-1][j-2]*inv2%p+inv2) %= p; }
else if(2*k<j)
{ (dp[i][j] += dp[i-1][j-2]*inv1%p) %= p; }
else
{ (dp[i][j] += dp[i-1][j]*inv2%p+dp[i-1][j-1]*inv2%p+inv2) %= p; }
}*/
}
}
for(re int i=2; i<=n*2+1; i++)
{ (ans += dp[n][i]*(x[i]-x[i-1])%p) %= p; /*printf("%lld\n",dp[n][i]);*/ }
printf("%lld\n",ans);
return 0;
}
}
signed main()
{ return OMA::main(); }
T3
注意到边的类型是单调变小的,考虑用两个并查集来维护边的种类,一个表示1类边,另一个表示1类边+2类边。
再维护需要经过3类边的点的个数,在维护修改时大力更新即可。
具体见代码。
初始化写锅调了好久QAQ
Code
#include<cstdio>
#define MAX 300010
#define re register
namespace OMA
{
int n,m,num[MAX];
struct graph
{
int next;
int to;
int w;
}edge[MAX<<1];
int cnt=1,head[MAX];
inline void add(int u,int v,int w)
{ edge[++cnt] = (graph){head[u],v,w},head[u] = cnt; }
struct DSU
{
int fa[MAX],size[MAX];
inline void start()
{
for(re int i=1; i<=n; i++)
{ fa[i] = i,size[i] = 1; }
}
inline int find(int x)
{ return x!=fa[x]?fa[x] = find(fa[x]):fa[x]; }
inline void merge(int x,int y)
{
int r1 = find(x),r2 = find(y);
if(r1!=r2)
{ fa[r1] = r2,size[r2] += size[r1]; }
}
}dsu1,dsu2;
int fa[MAX],sta[MAX];
inline void dfs(int u,int die,int state)
{
fa[u] = die,sta[u] = state;
for(re int i=head[u],v; i; i=edge[i].next)
{
v = edge[i].to;
if(v!=die)
{ dfs(v,u,edge[i].w); }
}
}
struct stream
{
template<typename type>inline stream &operator >>(type &s)
{
int w=1; s=0; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
return s*=w,*this;
}
}cin;
inline void swap(int &a,int &b)
{ int t=a; a=b; b=t; }
signed main()
{
cin >> n >> m;
for(re int i=1,u,v,w; i<=n-1; i++)
{ cin >> u >> v >> w; add(u,v,w),add(v,u,w); }
dfs(1,0,0);
dsu1.start(),dsu2.start();
for(re int i=1; i<=n; i++)
{
if(sta[i]==1)
{ dsu1.merge(i,fa[i]); }
if(sta[i]!=3)
{ dsu2.merge(i,fa[i]); }
}
for(re int i=1; i<=n; i++)
{
if(sta[i]==3)
{ num[dsu1.find(fa[i])] += dsu2.size[i]; }
}
//for(re int i=1; i<=n; i++)
//{ printf("sta[%d]=%d,num[%d]=%d %d %d\n",i,sta[i],i,num[i],dsu1.size[i],dsu2.size[i]); }
for(re int i=1,a,b,s,t; i<=m; i++)
{
cin >> a >> b >> s >> t;
if(fa[a]==b)
{ swap(a,b); }
if(sta[b]==2)
{
num[dsu1.find(a)] += num[b];
dsu1.merge(b,a);
}
if(sta[b]==3)
{
num[dsu1.find(a)] -= dsu2.size[b];
num[dsu1.find(fa[dsu2.find(a)])] += dsu2.size[b];
dsu2.merge(b,a);
}
if(sta[b]!=1)
{ sta[b]--; }
bool flag = false;
if(dsu2.find(s)==dsu2.find(t))
{ flag = true; }
if(dsu1.find(s)==dsu1.find(fa[dsu2.find(t)]))
{ flag = true; }
if(dsu2.find(fa[dsu1.find(s)])==dsu2.find(t))
{ flag = true; }
int ans = num[dsu1.find(s)]+dsu2.size[dsu2.find(s)];
if(sta[dsu1.find(s)]==3)
{ ans += dsu2.size[dsu2.find(fa[dsu1.find(s)])]; }
printf("%d %d\n",flag?1:0,ans);
}
return 0;
}
}
signed main()
{ return OMA::main(); }