银联高校极客挑战赛 初赛 第二场
开场几十分钟后才开始打的,实验室居然锁门了。。。
左上、右下角为(1,1)、(x,y)的矩阵的大小,dp处理
然后一个裸的二分答案
(1,1) (x,y) (x-k,y-k) 三个点
sum=
update:
题解说O(Tn^3)就是不严谨了。
20*300^3=540000000
这样都说能过!!!
如此接近1秒可以跑的数据量,
有时要考虑常数的大小,是否没跑满的情况!然而没有提及。
我的第二个代码:
n^2+(n-1)^2+...+1^1 = 1/6 * n*(n+1)*(2n+1)
相当于/3,实际是180901000,已经是极为接近了。
估计有些n^3的写法,也有这样的时间复杂度可以除以一个系数的情况。
而实际上呢,
有数据可以让它跑满吗?
可以的,如
然而出题人有这种闲情逸致弄这样的极限数据吗?
我不认为。。。
就像有些邀请赛,如2019南昌邀请赛。。。
(然而经过常数优化后,633ms过了)
搞个快读,多交几次,估计就能过去了。
有时也要考虑一下这种类型的情况。。。
另外一个更巧妙的O(n^3)防被卡方法:
右下端点按照可以制造的最大矩阵的大小,从大到小进行处理
避免全为'*'和全为'.'两个极端数据的卡
几十ms
code1:
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cmath> 4 #include <cstring> 5 #include <string> 6 #include <algorithm> 7 #include <iostream> 8 using namespace std; 9 #define ll long long 10 11 const double eps=1e-8; 12 const ll inf=1e9; 13 const ll mod=1e9+7; 14 const int maxn=3e2+10; 15 16 char str[maxn][maxn]; 17 int f[maxn][maxn]; 18 19 int main() 20 { 21 int t,n,m,l,r,mid,i,j; 22 bool vis; 23 scanf("%d",&t); 24 while (t--) 25 { 26 scanf("%d%d",&n,&m); 27 for (i=1;i<=n;i++) 28 scanf("%s",str[i]+1); 29 30 for (i=1;i<=n;i++) 31 for (j=1;j<=m;j++) 32 f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+(str[i][j]=='.'); 33 34 l=1,r=min(n,m); 35 while (l<=r) 36 { 37 mid=(l+r)>>1; 38 vis=0; 39 40 for (i=mid;i<=n;i++) 41 for (j=mid;j<=m;j++) 42 if (f[i][j]-f[i-mid][j]-f[i][j-mid]+f[i-mid][j-mid]==mid*mid) 43 vis=1; ///写成函数形成,遇到则退出true,这样会快一点;但是写起来不方便 44 if (vis) 45 l=mid+1; 46 else 47 r=mid-1; 48 } 49 printf("%d\n",r*r); 50 } 51 return 0; 52 }
code2:
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cmath> 4 #include <cstring> 5 #include <string> 6 #include <algorithm> 7 #include <iostream> 8 using namespace std; 9 #define ll long long 10 11 const double eps=1e-8; 12 const ll inf=1e9; 13 const ll mod=1e9+7; 14 const int maxn=3e2+10; 15 16 char str[maxn][maxn]; 17 int f[maxn][maxn]; 18 19 int main() 20 { 21 int t,n,m,i,j,k,l,r,g; 22 scanf("%d",&t); 23 while (t--) 24 { 25 scanf("%d%d",&n,&m); 26 for (i=1;i<=n;i++) 27 scanf("%s",str[i]+1); 28 29 for (i=1;i<=n;i++) 30 for (j=1;j<=m;j++) 31 f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+(str[i][j]=='.'); 32 33 /** 34 避免全为'*'和全为'.'两个极端数据的卡 35 **/ 36 37 r=0; 38 ///from large to small 39 for (i=n+m;i>r;i--) 40 { 41 for (j=max(1,i-m);j<=n;j++) 42 { 43 k=i-j; 44 g=min(j,k); 45 for (l=r+1;l<=g;l++) ///r+1来自乐逍大佬提醒 46 if (f[j][k]-f[j-l][k]-f[j][k-l]+f[j-l][k-l]!=l*l) 47 break; 48 if (l!=r+1) 49 r=l-1; 50 } 51 } 52 printf("%d\n",r*r); 53 } 54 return 0; 55 } 56 /* 57 1 58 2 3 59 ... 60 .** 61 62 3 4 63 **** 64 **** 65 **** 66 67 3 4 68 .... 69 .... 70 .... 71 72 4 4 73 .... 74 .... 75 .... 76 .... 77 */
n,m为独立的两部分,相乘即可
接下来是整除分块,对于同一块,余数是一个等差数列
update:
我只是找到了规律,
应该培养推公式的能力。。。
n%i 的数学式子
gcd 的数学式子
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cmath> 4 #include <cstring> 5 #include <string> 6 #include <algorithm> 7 #include <iostream> 8 using namespace std; 9 #define ll long long 10 11 const double eps=1e-8; 12 const ll inf=1e9; 13 const ll mod=1e9+7; 14 const int maxn=1e5+10; 15 16 ///sum(sum(i*j*(n mod i)(m mod j)) mod 1e9+7 17 ///1e9 18 19 int maxv=1e5; 20 21 //int zhi[maxn]; 22 bool vis[maxn]; 23 24 ll work(ll n) 25 { 26 ll l,r,sum=0,a0,b0,q,g,xx,yy,zz; 27 for (l=1;l<=n;l=r+1) 28 { 29 r=n/(n/l); 30 if (l==r) 31 sum+=n%l*l; 32 else 33 { 34 ///a0=l,b0,q,g 35 a0=l; 36 b0=n%l; 37 q=n/l; 38 g=r-l+1; 39 40 ///0^2+1^2+... 41 (sum+=a0*b0%mod*g)%=mod; 42 if (g&1) 43 (sum+=b0*g%mod*((g-1)/2))%=mod; 44 else 45 (sum+=b0*(g/2)%mod*(g-1))%=mod; 46 if (g&1) 47 (sum-=a0*q%mod*g%mod*((g-1)/2))%=mod; 48 else 49 (sum-=a0*q%mod*(g/2)%mod*(g-1))%=mod; 50 51 xx=g-1; 52 yy=g; 53 zz=2*g-1; 54 if (xx%2==0) 55 xx/=2; 56 else if (yy%2==0) 57 yy/=2; 58 else 59 zz/=2; 60 if (xx%3==0) 61 xx/=3; 62 else if (yy%3==0) 63 yy/=3; 64 else 65 zz/=3; 66 (sum-=q*xx%mod*yy%mod*zz)%=mod; 67 // q*(g-1)*g*(2*g-1)/6 68 ///sum+=a0*b0*g + b0*g*(g-1)/2 - a0*q*g*(g-1)/2 - q*(g-1)*g*(2*g-1)/6; 69 70 // sum+=a0*g-q*g*(g-1)/2; 71 } 72 // printf("%lld %lld\n",l,r); 73 } 74 return sum; 75 } 76 77 int main() 78 { 79 /* 80 ///n 最小的质因数 81 for (i=2;i<=maxv;i++) 82 { 83 if (!vis[i]) 84 { 85 zhi[++cnt_zhi]=i; 86 for (j) 87 } 88 } 89 */ 90 91 ll n,m,i; 92 ///two parts 93 scanf("%lld%lld",&n,&m); 94 printf("%lld",(work(n)*work(m)%mod+mod)%mod); 95 return 0; 96 } 97 /* 98 1 1 99 2 2 100 3 3 101 4 4 102 5 5 103 6 6 104 7 7 105 1000000000 1000000000 106 */
菜鸡不会推公式,只能找规律
打表
看这:
http://blog.csdn.net/morejarphone/article/details/50677172
https://www.cnblogs.com/dirge/p/5503289.html
无根树 & prufer
n^(n-2)
update:
我觉得没多少人能按照题解的方法写出来。。。
我感觉更多的是xxx,甚至是yyy。
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cmath> 4 #include <cstring> 5 #include <string> 6 #include <algorithm> 7 #include <iostream> 8 using namespace std; 9 #define ll long long 10 #include <set> 11 12 const double eps=1e-8; 13 const ll inf=1e9; 14 const ll mod=1e9+7; 15 const int maxn=15; 16 17 int n,a[maxn],hap[maxn],link[maxn],siz_n; 18 ll sum=0; 19 set<int>se; 20 21 void judge(int step) 22 { 23 int x,y; 24 if (step==n-1) 25 { 26 x=*se.begin(); 27 se.erase(se.begin()); 28 y=*se.begin(); 29 se.erase(se.begin()); 30 link[x]++,link[y]++; 31 for (x=1;x<=n;x++) 32 if (link[x]==1) 33 sum++; 34 return; 35 } 36 x=*se.begin(); 37 se.erase(se.begin()); 38 y=a[step]; 39 link[x]++,link[y]++; 40 hap[y]--; 41 if (hap[y]==0) 42 se.insert(y); 43 judge(step+1); 44 } 45 46 void work(int step) 47 { 48 int i; 49 if (step==n-1) 50 { 51 se.clear(); 52 memset(hap,0,siz_n); 53 memset(link,0,siz_n); 54 for (i=1;i<=n;i++) 55 hap[a[i]]++; 56 for (i=1;i<=n;i++) 57 if (hap[i]==0) 58 se.insert(i); 59 judge(1); 60 return; 61 } 62 for (i=1;i<=n;i++) 63 { 64 a[step]=i; 65 work(step+1); 66 } 67 } 68 69 int main() 70 { 71 for (n=2;n<=10;n++) 72 { 73 siz_n=4*(n+1); 74 sum=0; 75 work(1); 76 printf("n=%d sum=%lld\n",n,sum); 77 } 78 return 0; 79 } 80 /* 81 n=2 sum=2 82 n=3 sum=6 83 n=4 sum=36 84 n=5 sum=320 85 n=6 sum=3750 86 n=7 sum=54432 87 n=8 sum=941192 88 n=9 sum=18874368 89 90 n * (n-1)^(n-2) 91 */
代码
1 #include <cstdio> 2 #include <cstdlib> 3 #include <cmath> 4 #include <cstring> 5 #include <string> 6 #include <algorithm> 7 #include <iostream> 8 using namespace std; 9 #define ll long long 10 11 const double eps=1e-8; 12 const ll inf=1e9; 13 const ll mod=998244353; 14 const int maxn=1e5+10; 15 16 ll mul(ll a,ll b) 17 { 18 ll y=1; 19 while (b) 20 { 21 if (b&1) 22 y=y*a%mod; 23 a=a*a%mod; 24 b>>=1; 25 } 26 return y; 27 } 28 29 int main() 30 { 31 ll n; 32 scanf("%lld",&n); 33 if (n==1) 34 { 35 printf("1"); 36 return 0; 37 } 38 39 printf("%lld",n*mul(n-1,n-2)%mod); 40 return 0; 41 } 42 /* 43 n=2 sum=2 44 n=3 sum=6 45 n=4 sum=36 46 n=5 sum=320 47 n=6 sum=3750 48 n=7 sum=54432 49 n=8 sum=941192 50 n=9 sum=18874368 51 52 n * (n-1)^(n-2) 53 */
来自群里的大佬解答
证明:
1.不同的 大小为n-1的两个无根树,加上一个无根树后,还是无根树。
2.在一个无根树的两个不同的点,加上这个点,唯一与这个点相连的点的度数加一,与其它无根树不一样了。
引用另外的大佬一句总结