주윤달 NOIP 시뮬레이션 문제 주임비 문제풀이
9495 단어 시험 문제풀이
폭력 심수.설명 안 해.코드:
#include
#include
#include
#define f(x,y,z) for (int (x) = (y) ; x <= (z) ; x ++ )
using namespace std;
typedef long long ll;
const ll MOD = 1000000007;
const ll size = 100;
ll ans = 0;
ll a[size],b[size],c[size],d[size];
ll n;
ll dfs(ll nn,ll oi,ll whk)
{
if (nn == n+1)
{
if (ans < oi*whk) ans = oi * whk;
return 0;
}
if (oi - b[nn] > 0 ) dfs(nn+1 , oi-b[nn] , whk+a[nn]);
else dfs(nn+1, 0 ,whk+a[nn]);//
if (whk - d[nn] > 0 ) dfs(nn+1 , oi+c[nn] , whk-d[nn]);
else dfs(nn+1 , oi+c[nn] ,0 );// oi
}
int main(void)
{
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
scanf("%lld",&n);
for (ll i=1;i<=n;i++)
scanf("%lld%lld%lld%lld",&a[i],&b[i],&c[i],&d[i]);
dfs (1,0,0);
printf("%lld",ans);
return 0;
}
여기에 좋은 결론이 하나 있다. 나무 모양의 경로이기 때문에 한 구역 내의 연결 블록의 개수는 블록 안의 전체 검은 격자 수와 인접한 검은 블록의 대수를 빼는 것과 같다.처음에는 검은색 블록의 개수인 K개의 연결 블록이 있는데 한 쌍이 있으면 한 변을 연결하는 것과 같고 한 쌍이 있으면 하나의 연결 블록(나무 구조이기 때문)이 적기 때문에 연결 블록의 개수가 K-n쌍이라는 것을 증명할 수 있다.그리고 접두사와 통계, 통계변을 주의할 때 그 위나 왼쪽에 값을 주고 조회의 범위를 주의한다.사전 처리 복잡도 O(N2), 조회 복잡도 O(1).코드:
#include
#include
#include
#define f(x,y,z) for (int (x) = (y) ; x <= (z) ; x ++ )
using namespace std;
typedef int ll;
const ll MOD = 1000000007;
const ll size = 2010;
ll dop[size][size],l[size][size],up[size][size];
ll mat[size][size],tmp[size][size];
ll n,m,q;
char smat[size];
int main(void)
{
scanf("%d%d%d",&n,&m,&q);
for (ll i=1;i<=n;i++)
{
memset(smat,NULL,sizeof smat);
scanf("%s",smat);
for (ll j=1;j<=m;j++)
mat[i][j] = (int)(smat[j-1] - '0');
}
for (ll i=1;i<=n;i++)
for (ll j=1;j<=m;j++)
dop[i][j] = dop[i-1][j] + dop[i][j-1] - dop[i-1][j-1] + mat[i][j];
for (ll i=1;i<=n;i++)
for (ll j=1;j<=m;j++)
{
up[i][j] = up[i-1][j] + up[i][j-1] - up[i-1][j-1] ;
if (mat[i+1][j]==1 && mat[i][j]==1) ++up[i][j];
}
for (ll i=1;i<=n;i++)
for (ll j=1;j<=m;j++)
if (mat[i][j] == 1 && mat[i][j+1]==1) l[i][j] = l[i-1][j] + l[i][j-1] -l[i-1][j-1] + 1; else
l[i][j] = l[i-1][j] + l[i][j-1] - l[i-1][j-1];
while (q--)
{
ll x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
ll all = dop[x2][y2] + dop[x1-1][y1-1] - dop[x1-1][y2] - dop[x2][y1-1];
ll updot,ldot;
updot = up[x2-1][y2] + up[x1-1][y1-1] - up[x1-1][y2] - up[x2-1][y1-1];
ldot = l[x2][y2-1] + l[x1-1][y1-1] - l[x1-1][y2-1] - l[x2][y1-1];
ll ans = all-updot-ldot;
printf("%d
",ans);
}
return 0;
}
만약 한 점이 n줄의 선분에 의해 지나간다면, 그 귀축의 값은 n(n-4.1)/2이다.그래서 사실상 통계 역순이다. n개의 선이 지나가면 n(n-3-1)/2개의 역순이 있기 때문에 원래의 공식과 같다.그러나 이 문제는 공간이 넓기 때문에 우리는 하나하나의 유지보수, 즉 처음부터 끝까지 가장 가까운mod를 하나로 하고 그 다음은 다른 것으로 시작한다.계산할 때 줄 사이에만 역순이 있는 것을 계산한 다음에 공식을 밀어내면 끝난다.복잡도 O(N2 체인의 개수) = O(통과 가능).코드:
#include
#include
#include
#include
#define f(x,y,z) for (int (x) = (y) ; x <= (z) ; x ++
#define ni line[i].num
#define nj line[j].num
using namespace std;
typedef long long ll;
ll mod,n,begin,a,ans=0;
const ll MOD = 1000000007;
const ll size=100005;
struct WTF {
ll str, num;
inline ll operator * (const WTF &nima) {
ll ste = (str -nima.str + a) / a;
ll end = nima.num - ste + 1;
if (end >= num) return num * ( 2 * ste + num-1)/2;
return end * (ste + nima.num )/2 + (ll)nima.num * (num - end);
}
} line[size];ll cnt=0;
int main(void)
{
scanf("%lld%lld%lld%lld",&n,&begin,&a,&mod);
ll now = begin;ll y=0;
while (y1 - now) /a + 1;
if (y+num<=n)
{
line[cnt].num = num;
(now += num*a)%=mod;
y+=num;
}
else
{
line[cnt].num = n-y;
y = n;
}
}
for (ll i = 0;ifor (ll j = i+1 ; j<=cnt ; j++)
ans+=line[i] * line [j];
printf("%lld",ans);
return 0;
}
4