10 29
颓了好几天的blog 应该写写了 毕竟有些题目还是很不错的关键是dp 每次都推不出来 果然还是我太菜了.
今天 考的是牛客的比赛 质量还行 不过期望的分220 实际得分180/cy 好多神仙AK 各种崩溃 dp推不出来 图论不会写 很自闭反正... T1 100 T2 40 T3 40
LINK:简单博弈问题
这道题就很有意思了 典型的博弈问题 多个局面 考虑有向图游戏的和求SG函数 对于一个局面不同的石子个数不同 没有任何关系 仔细分析每一个局面。
对于1 必胜局 对于2 必败局 对于3 必败局...好像没什么规律 考虑偶数和奇数分开来看 偶数 我们后手是始终贴着先手下 可以发现只有先手不能走 所以偶数局面先手必败。
考虑大于1 的奇数局面 我们后手 的第一步下在先手第一步的棋子旁边的旁边 此时先手有两个位置不能下 而后手只有一个位置不能下 之后继续贴着先手下 可以发现还是先手败了 尽管能多下一个位置但是 被第一步反了一个位置形成类似于偶数的局面此时还是先手先下。
所以得到SG函数 除了1这个局面 其余所有局面都是0 然后求有向图游戏的和即可。
//单个局面偶数必败 //觉得单个局面除了1 剩下也是必败 直接异或好了 //#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define RI register ll #define db double #define pii pair<ll,ll> #define mk make_pair using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } int T; int n,ans; int main() { T=read(); while(T--) { ans=0; n=read(); for(int i=1;i<=n;++i) { int x=read(); if(x==1)ans^=1; } if(ans)puts("rabbit"); else puts("hamster"); } return 0; }
两个mingw 一遍补模拟赛的题目一遍写其他题目毕竟考了就要补。
LINK:树形dp问题
这道题还是很不错的引出了树上dp常用的两个方法 换根dp 和 up and down 都挺常见的 不过我最常用的是换根 这道题我决定两种方法都写一下。
首先是第一问 求以任意一个点为根的 深度不超过k的儿子的个数. 先考虑第一种比较不熟悉的方法 up and down。
1 预处理 设 f[i][j] 表示以i为根的子树内小于k的深度的点的个数。w[i][j]表示i向上j层的点数
2 预处理 设g[i][j]表示以i为根的子树内小于k的深度点的乘积不包括其本身 c[i][j]表示向上的不超过j的点的乘积 不包括其本身。
处理好之后 发现答案就有了/cy 转移挺难写的 一道非常有实际意义的题目。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define INF 1000000000 #define ll long long #define db double #define pb push_back #define un unsigned #define mod 1000000007 #define ull unsigned long long using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=100010; int n,k,len; int f[MAXN][11];//f[i][j]表示以i为根的子树内小于等于j的点的个数. int w[MAXN][11];//w[i][j]表示从i向上深度为不超过j的点的个数 ll g[MAXN][11];//g[i][j]表示以i为根的子树内部深度小于等于j的点的乘积 不包含其本身 ll c[MAXN][11];//c[i][j]表示从i向上深度不超过j的点的乘积 不包含其本身 int lin[MAXN],nex[MAXN<<1],ver[MAXN<<1]; inline void add(int x,int y) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; } inline ll fast_pow(ll b,ll p) { ll ans=1; while(p) { if(p&1)ans=ans*b%mod; b=b*b%mod; p=p>>1; } return ans; } inline void dfs(int x,int father) { for(int i=0;i<=k;++i)f[x][i]=1; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(tn==father)continue; dfs(tn,x); for(int j=1;j<=k;++j)f[x][j]+=f[tn][j-1]; } } inline void dfs1(int x,int father) { for(int i=0;i<=k;++i)g[x][i]=1; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(tn==father)continue; dfs1(tn,x); for(int j=1;j<=k;++j)g[x][j]=g[x][j]*g[tn][j-1]%mod*f[tn][j-1]%mod; } } inline void dp(int x,int father) { for(int i=0;i<=k;++i)w[x][i]+=1; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(tn==father)continue; for(int j=1;j<=k;++j) { w[tn][j]+=w[x][j-1]; if(j>=2)w[tn][j]+=f[x][j-1]-1-f[tn][j-2]; } dp(tn,x); } } inline void dp1(int x,int father) { for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(tn==father)continue; for(int j=1;j<=k;++j) { c[tn][j]=c[tn][j]*c[x][j-1]%mod*g[x][j-1]%mod; if(j>=2) { c[tn][j]=c[tn][j]*(w[x][j-1]+f[x][j-1]-1-f[tn][j-2])%mod; c[tn][j]=c[tn][j]*fast_pow(g[tn][j-2],mod-2)%mod; c[tn][j]=c[tn][j]*fast_pow(f[tn][j-2],mod-2)%mod; } } dp1(tn,x); } } int main() { //freopen("1.in","r",stdin); n=read();k=read(); for(int i=1;i<n;++i) { int x,y; x=read();y=read(); add(x,y);add(y,x); } dfs(1,0); //求出以某个点为根的深度不超过j的点的个数. dp(1,0); //for(int i=1;i<=n;++i)printf("%d ",f[i][k]); //for(int i=1;i<=n;++i)printf("%d ",w[i][k]); for(int i=1;i<=n;++i)printf("%d ",f[i][k]+w[i][k]-1); puts(""); dfs1(1,0); //求出以某个点位根的深度不超过j的点的个数. for(int i=1;i<=n;++i)for(int j=0;j<=k;++j)c[i][j]=1; dp1(1,0); //for(int i=1;i<=n;++i)printf("%d ",g[i][k]); //for(int i=1;i<=n;++i)printf("%d ",c[i][k]); for(int i=1;i<=n;++i)printf("%lld ",c[i][k]*g[i][k]%mod*(f[i][k]+w[i][k]-1)%mod); return 0; }
发现遇到这类问题非常的胆怯 好像是不敢去思考 这类问题我想应该不难只是自己懒的想这种私下跟其实是不可取的qwq。
首先 每张牌有两种选择 然后显然是一个二分图的问题 没多想直接暴力网络流了 加上测试点4的前缀和处理可以得到40分。
考虑满分做法 我们发现 一个事情 其实也可以选择匈牙利直接二分图的最大匹配。复杂度qn^2 我们可以顺势优化这个东西。
对于一个左端点 一定 有一个与之对应能表示出来的最大的右端点 然后左端点移动的同时右端点也同时移动 我们预处理这个东西 然后O(1) 回答询问即可。
复杂度n^2+Q 实测可过 原因 跑不满的缘故 我们由此想到用网络流优化这个过程复杂度可以达到nsqrt(n);
可实际上 网络流更换匹配的过程非常的复杂还需要修改一条流这个是很难做到的 所以基本上很难优化。当然直接匈牙利修改好修改 而且一定跑不满所以梦想匈牙利就过去了。
考虑真正的满分做法。这实际上是一个图论的问题 考虑每一张牌把两个花色连了起来 我们抽象成 几个连通块。
可以发现如果联通块是一颗树那么这棵树 这几个节点都不能被同时体现。考虑一下询问 一个询问的不合法当且仅当其包含了一棵完整的树。
这样的话 如何判断一条线段包含了 这样的一棵树 显然 对于一棵树尽管可能是不连续的 但是 判断包含关系只需要 找到树的编号的最大值及最小值这个线段包含树的两端即可。
那么 就是 一个经典问题了 线段之间的覆盖问题。 显然 我们离线一下利用树状数组即可。复杂度mlogn.
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define RI register ll #define db double #define pii pair<ll,ll> #define mk make_pair #define mod 1000000007 using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=100010; int n,m,len,minn,maxx; int q[MAXN],f[MAXN],vis[MAXN],c[MAXN],ans[MAXN],top; int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1]; inline void add(int x,int y) { ver[++len]=y; nex[len]=lin[x]; lin[x]=len; } inline int getfather(int x){return x==f[x]?x:f[x]=getfather(f[x]);} inline void dfs(int x,int father) { maxx=max(maxx,x); minn=min(minn,x); for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(tn==father)continue; dfs(tn,x); } } struct wy { int x,y; int id; int friend operator <(wy a,wy b) { return a.x<b.x; } }t[MAXN],w[MAXN]; inline void add1(int x,int y) { while(x<=n) { c[x]+=y; x+=x&(-x); } return; } inline int ask(int x) { int cnt=0; while(x) { cnt+=c[x]; x-=x&(-x); } return cnt; } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;++i)f[i]=i; for(int i=1;i<=m;++i) { int x,y; x=read();y=read(); add(x,y);add(y,x); int xx=getfather(x); int yy=getfather(y); if(xx==yy)vis[xx]=1; f[xx]=yy; } for(int i=1;i<=n;++i) { int x=getfather(i); if(vis[x])continue; q[++top]=i; vis[x]=1; } //for(int i=1;i<=top;++i)printf("%d\n",q[i]); for(int i=1;i<=top;++i) { minn=INF,maxx=0; dfs(q[i],0); w[i]=(wy){minn,maxx}; add1(maxx,1); } m=read(); for(int i=1;i<=m;++i) { int x,y; x=read();y=read(); t[i]=(wy){x,y,i}; } sort(t+1,t+1+m); sort(w+1,w+1+top); int flag=1; for(int i=1;i<=m;++i) { while(flag<=top&&w[flag].x<t[i].x) { add1(w[flag].y,-1); ++flag; } ans[t[i].id]=ask(t[i].y); } for(int i=1;i<=m;++i)puts(ans[i]?"No":"Yes"); return 0; }